From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id WBzjAXvHmGmCLwMAWB0awg (envelope-from ) for ; Fri, 20 Feb 2026 15:43:39 -0500 Authentication-Results: simark.ca; dkim=fail reason="signature verification failed" (768-bit key; unprotected) header.d=tromey.com header.i=@tromey.com header.a=rsa-sha256 header.s=default header.b=ZNHOZCj0; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id 87E521E0BA; Fri, 20 Feb 2026 15:43:38 -0500 (EST) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-0.8 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_00, DKIM_INVALID,DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_BL_SPAMCOP_NET, RCVD_IN_DNSWL_MED,RCVD_IN_VALIDITY_CERTIFIED_BLOCKED, RCVD_IN_VALIDITY_RPBL_BLOCKED,RCVD_IN_VALIDITY_SAFE_BLOCKED autolearn=no autolearn_force=no version=4.0.1 Received: from vm01.sourceware.org (vm01.sourceware.org [38.145.34.32]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by simark.ca (Postfix) with ESMTPS id B10E81E08D for ; Fri, 20 Feb 2026 15:43:33 -0500 (EST) Received: from vm01.sourceware.org (localhost [127.0.0.1]) by sourceware.org (Postfix) with ESMTP id 30B3F4BAD15F for ; Fri, 20 Feb 2026 20:43:33 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 30B3F4BAD15F Authentication-Results: sourceware.org; dkim=fail reason="signature verification failed" (768-bit key, unprotected) header.d=tromey.com header.i=@tromey.com header.a=rsa-sha256 header.s=default header.b=ZNHOZCj0 Received: from omta40.uswest2.a.cloudfilter.net (omta40.uswest2.a.cloudfilter.net [35.89.44.39]) by sourceware.org (Postfix) with ESMTPS id 2DFFD4B9DB69 for ; Fri, 20 Feb 2026 20:42:29 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 2DFFD4B9DB69 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=tromey.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=tromey.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 2DFFD4B9DB69 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=35.89.44.39 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1771620149; cv=none; b=bX304f8P8oW/a0fItmxtD9xQOllfF+LOMPaJjX/2syAlmWSHHNgL8eXozdhet518ojydD9+d8ZPM2ZXeKeqyD6Gr042scWUqgMAmKpYUL0/P0i2W0VvqJ5Bf2eHdn/5DDApX1R/LGJ8gh3Wt3Ex2EMFn5DMshvhqXMWyn6aNprg= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1771620149; c=relaxed/simple; bh=lp1kiqc32QH/6ghlfCIYq2hfztnrrZJO0IeYxwQcai0=; h=DKIM-Signature:From:Date:Subject:MIME-Version:Message-Id:To; b=AiNs2dmlCrhtbbBhkV550G2rxZKBjkwODgow7wjSczH4ywanSzPZ+7cgcxrM4nJL3vdDvXv5pJviqbIcL12+NIs8uRZ+aXGGHfEHUTlFQSDopaoDz4gn1igRGsa4EGhFORQrfXWrd54Ji4BOcv1HH25vqC1C+tHCVsxdJShQy5A= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 2DFFD4B9DB69 Received: from eig-obgw-5007b.ext.cloudfilter.net ([10.0.29.167]) by cmsmtp with ESMTPS id tDwBvB4lOaPqLtXKWvKHkW; Fri, 20 Feb 2026 20:42:28 +0000 Received: from box5379.bluehost.com ([162.241.216.53]) by cmsmtp with ESMTPS id tXKTvZhuxV6WMtXKUv73Nr; Fri, 20 Feb 2026 20:42:26 +0000 X-Authority-Analysis: v=2.4 cv=E//Npbdl c=1 sm=1 tr=0 ts=6998c733 a=ApxJNpeYhEAb1aAlGBBbmA==:117 a=ApxJNpeYhEAb1aAlGBBbmA==:17 a=IkcTkHD0fZMA:10 a=HzLeVaNsDn8A:10 a=ItBw4LHWJt0A:10 a=CCpqsmhAAAAA:8 a=mDV3o1hIAAAA:8 a=Qj3xCFtzMH30rGfrVWoA:9 a=QEXdDO2ut3YA:10 a=ul9cdbp4aOFLsgKbc677:22 a=DCx65vhANUyCzuf5D8fC:22 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=tromey.com; s=default; h=Cc:To:In-Reply-To:References:Message-Id: Content-Transfer-Encoding:Content-Type:MIME-Version:Subject:Date:From:Sender: Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender :Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=/5/SaZ9I+g0ljdIfkrJCeV+c5YbhMq7QzkYWT/CSUGE=; b=ZNHOZCj0gIlNfKwF8SLUwUA+gd a/kHuFJVGppBO2RmXrgwRdR3E1EUY/2naOD1UzDcKsTZ+QnMXUc5qFWSUrFy1IZ5sQspLVf2XDsfr iwYIguRwL5UpoduCtmoU2mKf5; Received: from 97-122-122-234.hlrn.qwest.net ([97.122.122.234]:49908 helo=[192.168.122.1]) by box5379.bluehost.com with esmtpsa (TLS1.3) tls TLS_AES_256_GCM_SHA384 (Exim 4.98.2) (envelope-from ) id 1vtXKT-00000000Jtq-1Yme; Fri, 20 Feb 2026 13:42:25 -0700 From: Tom Tromey Date: Fri, 20 Feb 2026 13:42:27 -0700 Subject: [PATCH v3 7/8] Handle inline functions with dwz MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Message-Id: <20260220-dw-inline-fixup-pr-symtab-30728-2-v3-7-98ae8ab28fab@tromey.com> References: <20260220-dw-inline-fixup-pr-symtab-30728-2-v3-0-98ae8ab28fab@tromey.com> In-Reply-To: <20260220-dw-inline-fixup-pr-symtab-30728-2-v3-0-98ae8ab28fab@tromey.com> To: gdb-patches@sourceware.org Cc: Tom Tromey X-Mailer: b4 0.14.3 X-AntiAbuse: This header was added to track abuse, please include it with any abuse report X-AntiAbuse: Primary Hostname - box5379.bluehost.com X-AntiAbuse: Original Domain - sourceware.org X-AntiAbuse: Originator/Caller UID/GID - [47 12] / [47 12] X-AntiAbuse: Sender Address Domain - tromey.com X-BWhitelist: no X-Source-IP: 97.122.122.234 X-Source-L: No X-Exim-ID: 1vtXKT-00000000Jtq-1Yme X-Source: X-Source-Args: X-Source-Dir: X-Source-Sender: 97-122-122-234.hlrn.qwest.net ([192.168.122.1]) [97.122.122.234]:49908 X-Source-Auth: tom+tromey.com X-Email-Count: 11 X-Org: HG=bhshared;ORG=bluehost; X-Source-Cap: ZWx5bnJvYmk7ZWx5bnJvYmk7Ym94NTM3OS5ibHVlaG9zdC5jb20= X-Local-Domain: yes X-CMAE-Envelope: MS4xfPDHiasc3eHgqNErAC1E1SPhNv/ayitMoLi9mbxAH11CqHk3g98f/VD3cIIk4bf5NMeBb1qEqTD6pbCGEN4aMekk/trsETKCgr5M1qUjckajGGOVRgwv l2WZf+6ushIPQ9hS1dKRDpPC2PiZO5noXFgVAbwI7VUv0uVJgo/cN//XtIRnWhLsYTci2ialhDzMEK6/ftP9dVtxatGkCZuwk58= X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces~public-inbox=simark.ca@sourceware.org Currently, gdb does not properly handle inline functions when dwz is used. This can be seen by running gdb.cp/breakpoint-locs.exp with the cc-with-dwz target board. The problem here is that inline functions need special handling in the dwz case. First, recall that a DWARF partial unit cannot, in general, be read in isolation, as it may not have a language. To handle this, gdb defers scanning partial units directly, and instead scans them in the context of some including CU. Entries coming from the PU are attributed to this reading CU. If multiple CUs import a PU, gdb has the reader threads race to see which one does the actual reading. However, if an inline function is moved into a partial unit, then that means it has potentially been inlined somewhere in every including CU. Thus, when linespec searches for this function, each such including CU should be expanded. But because gdb only attributed the function's index entry to one CU, only that particular one is expanded. This patch fixes this bug. All inclusions of a PU are recorded. Entries coming from a PU are attributed to that PU. For most entries coming from the PU, a single "canonical" outer CU is chosen to expand. However, when an inline function is found, all such CUs are expanded. A change was also needed to the index writer to handle this case. There, entries coming from a PU should note the correct including CU. This must be done because, with .debug_names or .gdb_index, gdb does not have information about unit imports. Handling inline functions here means writing out a separate entry for each outermost CU that includes the function's PU. I did consider changing the cooked indexer to create an internal table more similar to what would be created by the .debug_names (e.g.) reader: that is, multiple entries for each inline function. However, this seemed more expensive at read time, and a main goal of the cooked indexer is to be fast. Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=30728 --- gdb/dwarf2/cooked-index-entry.c | 24 +++++ gdb/dwarf2/cooked-index-entry.h | 39 ++++++- gdb/dwarf2/cooked-index-worker.c | 14 +++ gdb/dwarf2/cooked-index-worker.h | 26 +++-- gdb/dwarf2/cooked-indexer.c | 54 +++++++--- gdb/dwarf2/cooked-indexer.h | 8 +- gdb/dwarf2/index-write.c | 223 ++++++++++++++++++++++----------------- gdb/dwarf2/read.c | 49 +++++++-- gdb/dwarf2/read.h | 37 +++++++ 9 files changed, 339 insertions(+), 135 deletions(-) diff --git a/gdb/dwarf2/cooked-index-entry.c b/gdb/dwarf2/cooked-index-entry.c index c24dcf445fe..2f61e5c4c57 100644 --- a/gdb/dwarf2/cooked-index-entry.c +++ b/gdb/dwarf2/cooked-index-entry.c @@ -18,6 +18,7 @@ along with this program. If not, see . */ #include "dwarf2/cooked-index-entry.h" +#include "dwarf2/read.h" #include "dwarf2/tag.h" #include "gdbsupport/selftest.h" @@ -33,6 +34,7 @@ to_string (cooked_index_flag flags) MAP_ENUM_FLAG (IS_TYPE_DECLARATION), MAP_ENUM_FLAG (IS_PARENT_DEFERRED), MAP_ENUM_FLAG (IS_SYNTHESIZED), + MAP_ENUM_FLAG (IS_INLINED), }; return flags.to_string (mapping); @@ -240,6 +242,28 @@ cooked_index_entry::write_scope (struct obstack *storage, obstack_grow (storage, sep, strlen (sep)); } +/* See cooked_index_entry.h. */ + +void +cooked_index_entry::force_set_language () const +{ + gdb_assert (lang == language_unknown); + lang = per_cu->lang (); +} + +/* See cooked-index-entry.h. */ + +bool +cooked_index_entry::visit_defining_cus (per_cu_callback callback) const +{ + if ((flags & IS_INLINED) == 0) + return callback (per_cu->canonical_outermost_cu ()); + + return per_cu->recursively_visit_cus (callback); +} + +/* See cooked-index-entry.h. */ + INIT_GDB_FILE (dwarf2_entry) { #if GDB_SELF_TEST diff --git a/gdb/dwarf2/cooked-index-entry.h b/gdb/dwarf2/cooked-index-entry.h index a64b2674056..9eed2ca9c46 100644 --- a/gdb/dwarf2/cooked-index-entry.h +++ b/gdb/dwarf2/cooked-index-entry.h @@ -44,6 +44,9 @@ enum cooked_index_flag_enum : unsigned char /* True if this entry was synthesized by gdb (as opposed to coming directly from the DWARF). */ IS_SYNTHESIZED = 32, + /* True if this is a function that has DW_AT_inline set in a way + that indicates it was inlined. */ + IS_INLINED = 64, }; DEF_ENUM_FLAGS_TYPE (enum cooked_index_flag_enum, cooked_index_flag); @@ -222,6 +225,31 @@ struct cooked_index_entry : public allocate_on_obstack return m_parent_entry.deferred; } + /* Force the language to be set to the CU's language. This may only + be called when the language is unknown, which can only happen + with .gdb_index. This method is const because it is called from + a location that normally handles const entries. */ + void force_set_language () const; + + /* Type of callback used when visiting defining CUs. */ + using per_cu_callback = gdb::function_view; + + /* Calls CALLBACK for each CU that "defines" this entry. + + If any call to CALLBACK returns false, this immediately returns + false (skipping the remaining calls); otherwise returns true. + + For most entries, there is a single defining CU, which is the + canonical outermost includer of the CU holding the entry. In the + most typical case (i.e., where "dwz" has not been used), this is + just the entry's CU. + + However, conceptually an inlined subroutine is defined in each + such outermost includer -- if a subroutine is inlined in many + places, and then "dwz" is run, the IS_INLINED subroutine may be + defined in some CU that is included by many other CUs. */ + bool visit_defining_cus (per_cu_callback callback) const; + /* The name as it appears in DWARF. This always points into one of the mapped DWARF sections. Note that this may be the name or the linkage name -- two entries are created for DIEs which have both @@ -233,11 +261,16 @@ struct cooked_index_entry : public allocate_on_obstack enum dwarf_tag tag; /* Any flags attached to this entry. */ cooked_index_flag flags; - /* The language of this symbol. */ - ENUM_BITFIELD (language) lang : LANGUAGE_BITS; + /* The language of this symbol. This is mutable because there is a + situation where the language is initially unknown, and then only + filled in later. In particular this can happen when using + .gdb_index. See also cooked_index_functions::search. */ + mutable ENUM_BITFIELD (language) lang : LANGUAGE_BITS; /* The offset of this DIE. */ sect_offset die_offset; - /* The CU from which this entry originates. */ + /* The CU from which this entry originates. This may point to a + partial CU, which can't be expanded in isolation; see + visit_defining_cus. */ dwarf2_per_cu *per_cu; private: diff --git a/gdb/dwarf2/cooked-index-worker.c b/gdb/dwarf2/cooked-index-worker.c index ef2427d0a71..723e027172e 100644 --- a/gdb/dwarf2/cooked-index-worker.c +++ b/gdb/dwarf2/cooked-index-worker.c @@ -105,6 +105,16 @@ cooked_index_worker_result::emit_complaints_and_exceptions /* See cooked-index-worker.h. */ +void +cooked_index_worker_result::invert_cu_inclusions () +{ + for (auto &iter : m_inclusions) + for (dwarf2_per_cu *included : iter.second) + included->add_includer (iter.first); +} + +/* See cooked-index-worker.h. */ + void cooked_index_worker::start () { @@ -239,6 +249,10 @@ cooked_index_worker::done_reading () m_all_parents_map.add_map (*one_result.get_parent_map ()); } + /* Update all the CU inclusion information. */ + for (auto &item : m_results) + item.invert_cu_inclusions (); + dwarf2_per_bfd *per_bfd = m_per_objfile->per_bfd; cooked_index *table = (gdb::checked_static_cast diff --git a/gdb/dwarf2/cooked-index-worker.h b/gdb/dwarf2/cooked-index-worker.h index d46415bf8bc..1799c0b9e08 100644 --- a/gdb/dwarf2/cooked-index-worker.h +++ b/gdb/dwarf2/cooked-index-worker.h @@ -66,17 +66,6 @@ class cooked_index_worker_result /* Add an entry to the index. The arguments describe the entry; see cooked-index.h. The new entry is returned. */ - cooked_index_entry *add (sect_offset die_offset, enum dwarf_tag tag, - cooked_index_flag flags, - const char *name, - cooked_index_entry_ref parent_entry, - dwarf2_per_cu *per_cu) - { - return m_shard->add (die_offset, tag, flags, per_cu->lang (), - name, parent_entry, per_cu); - } - - /* Overload that allows the language to be specified. */ cooked_index_entry *add (sect_offset die_offset, enum dwarf_tag tag, cooked_index_flag flags, enum language lang, const char *name, @@ -154,6 +143,16 @@ class cooked_index_worker_result void emit_complaints_and_exceptions (gdb::unordered_set &seen_exceptions); + /* Record that the FROM CU included the TO CU. */ + void record_inclusion (dwarf2_per_cu *from, dwarf2_per_cu *to) + { + m_inclusions[from].push_back (to); + } + + /* Update CUs with all the inclusion information that we've + discovered. */ + void invert_cu_inclusions (); + private: /* The abbrev table cache used by this indexer. */ abbrev_table_cache m_abbrev_table_cache; @@ -198,6 +197,11 @@ class cooked_index_worker_result /* Exceptions that we're storing to emit later. */ std::vector m_exceptions; + + /* Map from a CU to a list of all the CUs that it directly + includes. */ + gdb::unordered_map> m_inclusions; }; /* The possible states of the index. See the explanatory comment diff --git a/gdb/dwarf2/cooked-indexer.c b/gdb/dwarf2/cooked-indexer.c index 3221ccd34f8..26be0aff7bf 100644 --- a/gdb/dwarf2/cooked-indexer.c +++ b/gdb/dwarf2/cooked-indexer.c @@ -27,9 +27,8 @@ /* See cooked-indexer.h. */ cooked_indexer::cooked_indexer (cooked_index_worker_result *storage, - dwarf2_per_cu *per_cu, enum language language) + enum language language) : m_index_storage (storage), - m_per_cu (per_cu), m_language (language), m_die_range_map (storage->get_parent_map ()) { @@ -102,6 +101,17 @@ cooked_indexer::ensure_cu_exists (cutu_reader *reader, Doing this check here avoids self-imports as well. */ if (for_scanning) { + /* Record the inclusion precisely here. Inclusions are done to + ensure that imported units cause the correct expansions when + searched; and in particular that all including units are + expanded when finding an inline function. So, this must be + done for all units that are importing "for scanning". + However, doing this any later than this spot would be too + late -- because we want to do this for all importing units, + not just the one that happens to win the race to do the + scanning. */ + m_index_storage->record_inclusion (reader->cu ()->per_cu, per_cu); + bool nope = false; if (!per_cu->scanned.compare_exchange_strong (nope, true)) return nullptr; @@ -146,6 +156,7 @@ cooked_indexer::scan_attributes (dwarf2_per_cu *scanning_per_cu, const cooked_index_entry **parent_entry, parent_map::addr_type *maybe_defer, bool *is_enum_class, + bool *is_inlined, bool for_specification) { bool is_declaration = false; @@ -281,6 +292,16 @@ cooked_indexer::scan_attributes (dwarf2_per_cu *scanning_per_cu, scanning_per_cu, abbrev->tag); } break; + + case DW_AT_inline: + if (abbrev->tag == DW_TAG_subprogram) + { + std::optional val = attr.unsigned_constant (); + if (val.has_value () && (*val == DW_INL_inlined + || *val == DW_INL_declared_inlined)) + *is_inlined = true; + } + break; } } @@ -359,7 +380,7 @@ cooked_indexer::scan_attributes (dwarf2_per_cu *scanning_per_cu, scan_attributes (scanning_per_cu, new_reader, new_info_ptr, new_info_ptr, new_abbrev, name, linkage_name, flags, nullptr, parent_entry, maybe_defer, - is_enum_class, true); + is_enum_class, is_inlined, true); } if (!for_specification) @@ -427,8 +448,7 @@ cooked_indexer::index_imported_unit (cutu_reader *reader, if (!target.has_value ()) return info_ptr; - cutu_reader *new_reader - = ensure_cu_exists (reader, *target, true); + cutu_reader *new_reader = ensure_cu_exists (reader, *target, true); if (new_reader != nullptr) index_dies (new_reader, new_reader->info_ptr (), nullptr, false); @@ -526,6 +546,7 @@ cooked_indexer::index_dies (cutu_reader *reader, sect_offset sibling {}; const cooked_index_entry *this_parent_entry = parent_entry; bool is_enum_class = false; + bool is_inlined = false; /* The scope of a DW_TAG_entry_point cooked_index_entry is the one of its surrounding subroutine. */ @@ -534,7 +555,8 @@ cooked_indexer::index_dies (cutu_reader *reader, info_ptr = scan_attributes (reader->cu ()->per_cu, reader, info_ptr, info_ptr, abbrev, &name, &linkage_name, &flags, &sibling, - &this_parent_entry, &defer, &is_enum_class, false); + &this_parent_entry, &defer, &is_enum_class, + &is_inlined, false); /* A DW_TAG_entry_point inherits its static/extern property from the enclosing subroutine. */ if (abbrev->tag == DW_TAG_entry_point) @@ -583,18 +605,25 @@ cooked_indexer::index_dies (cutu_reader *reader, } } + if (is_inlined) + flags |= IS_INLINED; + cooked_index_entry *this_entry = nullptr; + /* Always use the reader's CU for the entry CU. */ + dwarf2_per_cu *cu_for_entry = reader->cu ()->per_cu; if (name != nullptr) { if (defer != 0) this_entry = m_index_storage->add (this_die, abbrev->tag, - flags | IS_PARENT_DEFERRED, name, - defer, m_per_cu); + flags | IS_PARENT_DEFERRED, + m_language, name, + defer, cu_for_entry); else this_entry - = m_index_storage->add (this_die, abbrev->tag, flags, name, - this_parent_entry, m_per_cu); + = m_index_storage->add (this_die, abbrev->tag, flags, + m_language, name, + this_parent_entry, cu_for_entry); } if (linkage_name != nullptr) @@ -607,11 +636,10 @@ cooked_indexer::index_dies (cutu_reader *reader, have linkage name present but name is absent. */ if (name != nullptr || (abbrev->tag != DW_TAG_subprogram - && abbrev->tag != DW_TAG_inlined_subroutine && abbrev->tag != DW_TAG_entry_point)) flags = flags | IS_LINKAGE; - m_index_storage->add (this_die, abbrev->tag, flags, - linkage_name, nullptr, m_per_cu); + m_index_storage->add (this_die, abbrev->tag, flags, m_language, + linkage_name, nullptr, cu_for_entry); } if (abbrev->has_children) diff --git a/gdb/dwarf2/cooked-indexer.h b/gdb/dwarf2/cooked-indexer.h index 5cb5bae33f1..563742c90f6 100644 --- a/gdb/dwarf2/cooked-indexer.h +++ b/gdb/dwarf2/cooked-indexer.h @@ -37,7 +37,7 @@ struct section_and_offset; class cooked_indexer { public: - cooked_indexer (cooked_index_worker_result *storage, dwarf2_per_cu *per_cu, + cooked_indexer (cooked_index_worker_result *storage, enum language language); DISABLE_COPY_AND_ASSIGN (cooked_indexer); @@ -85,6 +85,7 @@ class cooked_indexer const cooked_index_entry **parent_entry, parent_map::addr_type *maybe_defer, bool *is_enum_class, + bool *is_inlined, bool for_specification); /* Handle DW_TAG_imported_unit, by scanning the DIE to find @@ -104,11 +105,6 @@ class cooked_indexer /* The storage object, where the results are kept. */ cooked_index_worker_result *m_index_storage; - /* The CU that we are reading on behalf of. This object might be - asked to index one CU but to treat the results as if they come - from some including CU; in this case the including CU would be - recorded here. */ - dwarf2_per_cu *m_per_cu; /* The language that we're assuming when reading. */ enum language m_language; diff --git a/gdb/dwarf2/index-write.c b/gdb/dwarf2/index-write.c index c4e75898f4c..05ccf3c1473 100644 --- a/gdb/dwarf2/index-write.c +++ b/gdb/dwarf2/index-write.c @@ -683,6 +683,10 @@ class debug_names /* The next available abbrev number. */ int next_abbrev = 1; + /* Storage for the CU list for inline entries. This is outside + the loops to try to avoid repeated allocations. */ + std::vector per_cus; + for (auto &[name, these_entries] : m_name_to_value_set) { /* Sort the items within each bucket. This ensures that the @@ -711,108 +715,136 @@ class debug_names for (const cooked_index_entry *entry : these_entries) { - unit_kind kind = (entry->per_cu->is_debug_types () - ? unit_kind::tu - : unit_kind::cu); - /* Some Ada parentage is synthesized by the reader and so - must be ignored here. */ - const cooked_index_entry *parent = entry->get_parent (); - if (parent != nullptr && (parent->flags & IS_SYNTHESIZED) != 0) - parent = nullptr; - - int &abbrev = m_indexkey_to_abbrev[index_key (entry->tag, - kind, - entry->flags, - entry->lang, - parent != nullptr)]; - if (abbrev == 0) + /* Accumulate all the relevant CUs. This is needed for + the DW_TAG_inlined_subroutine special case. */ + per_cus.clear (); + entry->visit_defining_cus ([&] (dwarf2_per_cu *one_cu) + { + per_cus.push_back (one_cu); + return true; + }); + /* Make sure the output is stable. */ + std::sort (per_cus.begin (), per_cus.end (), + [] (const dwarf2_per_cu *lhs, const dwarf2_per_cu *rhs) + { + return lhs->index < rhs->index; + }); + + for (dwarf2_per_cu *one_cu : per_cus) { - abbrev = next_abbrev++; + unit_kind kind = (entry->per_cu->is_debug_types () + ? unit_kind::tu + : unit_kind::cu); + /* Some Ada parentage is synthesized by the reader and so + must be ignored here. */ + const cooked_index_entry *parent = entry->get_parent (); + if (parent != nullptr && (parent->flags & IS_SYNTHESIZED) != 0) + parent = nullptr; + + int &abbrev = m_indexkey_to_abbrev[index_key (entry->tag, + kind, + entry->flags, + entry->lang, + parent != nullptr)]; + if (abbrev == 0) + { + abbrev = next_abbrev++; + + /* Abbrev number and tag. */ + m_abbrev_table.append_unsigned_leb128 (abbrev); + m_abbrev_table.append_unsigned_leb128 (entry->tag); + + /* Unit index. */ + m_abbrev_table.append_unsigned_leb128 + (kind == unit_kind::cu + ? DW_IDX_compile_unit + : DW_IDX_type_unit); + m_abbrev_table.append_unsigned_leb128 (DW_FORM_udata); + + /* DIE offset. */ + m_abbrev_table.append_unsigned_leb128 (DW_IDX_die_offset); + m_abbrev_table.append_unsigned_leb128 (DW_FORM_ref_addr); + + /* Language. */ + m_abbrev_table.append_unsigned_leb128 + (DW_IDX_GNU_language); + m_abbrev_table.append_unsigned_leb128 (DW_FORM_udata); + + /* Internal linkage flag. */ + if (!tag_is_type (entry->tag) + && (entry->flags & IS_STATIC) != 0) + { + m_abbrev_table.append_unsigned_leb128 + (DW_IDX_GNU_internal); + m_abbrev_table.append_unsigned_leb128 + (DW_FORM_flag_present); + } + + /* Main subprogram flag. */ + if ((entry->flags & IS_MAIN) != 0) + { + m_abbrev_table.append_unsigned_leb128 + (DW_IDX_GNU_main); + m_abbrev_table.append_unsigned_leb128 + (DW_FORM_flag_present); + } + + /* Linkage name flag. */ + if ((entry->flags & IS_LINKAGE) != 0) + { + m_abbrev_table.append_unsigned_leb128 + (DW_IDX_GNU_linkage_name); + m_abbrev_table.append_unsigned_leb128 + (DW_FORM_flag_present); + } + + /* Parent offset. */ + if (parent != nullptr) + { + m_abbrev_table.append_unsigned_leb128 (DW_IDX_parent); + m_abbrev_table.append_unsigned_leb128 (DW_FORM_data4); + } + + /* Terminate attributes list. */ + m_abbrev_table.append_unsigned_leb128 (0); + m_abbrev_table.append_unsigned_leb128 (0); + } - /* Abbrev number and tag. */ - m_abbrev_table.append_unsigned_leb128 (abbrev); - m_abbrev_table.append_unsigned_leb128 (entry->tag); + /* Record the offset in the pool at which this entry will + reside. */ + const auto offset_inserted + = (m_entry_pool_offsets.emplace (entry, m_entry_pool.size ()) + .second); + gdb_assert (offset_inserted); + + /* Write the entry to the pool, starting with the + abbrev number. */ + m_entry_pool.append_unsigned_leb128 (abbrev); /* Unit index. */ - m_abbrev_table.append_unsigned_leb128 - (kind == unit_kind::cu - ? DW_IDX_compile_unit - : DW_IDX_type_unit); - m_abbrev_table.append_unsigned_leb128 (DW_FORM_udata); + const auto it = m_cu_index_htab.find (one_cu); + gdb_assert (it != m_cu_index_htab.cend ()); + m_entry_pool.append_unsigned_leb128 (it->second); /* DIE offset. */ - m_abbrev_table.append_unsigned_leb128 (DW_IDX_die_offset); - m_abbrev_table.append_unsigned_leb128 (DW_FORM_ref_addr); + m_entry_pool.append_uint (dwarf5_offset_size (), + m_dwarf5_byte_order, + to_underlying (entry->die_offset)); /* Language. */ - m_abbrev_table.append_unsigned_leb128 (DW_IDX_GNU_language); - m_abbrev_table.append_unsigned_leb128 (DW_FORM_udata); - - /* Internal linkage flag. */ - if (!tag_is_type (entry->tag) - && (entry->flags & IS_STATIC) != 0) - { - m_abbrev_table.append_unsigned_leb128 (DW_IDX_GNU_internal); - m_abbrev_table.append_unsigned_leb128 (DW_FORM_flag_present); - } - - /* Main subprogram flag. */ - if ((entry->flags & IS_MAIN) != 0) - { - m_abbrev_table.append_unsigned_leb128 (DW_IDX_GNU_main); - m_abbrev_table.append_unsigned_leb128 (DW_FORM_flag_present); - } - - /* Linkage name flag. */ - if ((entry->flags & IS_LINKAGE) != 0) - { - m_abbrev_table.append_unsigned_leb128 (DW_IDX_GNU_linkage_name); - m_abbrev_table.append_unsigned_leb128 (DW_FORM_flag_present); - } + m_entry_pool.append_unsigned_leb128 + (entry->per_cu->dw_lang ()); /* Parent offset. */ if (parent != nullptr) { - m_abbrev_table.append_unsigned_leb128 (DW_IDX_parent); - m_abbrev_table.append_unsigned_leb128 (DW_FORM_data4); - } - - /* Terminate attributes list. */ - m_abbrev_table.append_unsigned_leb128 (0); - m_abbrev_table.append_unsigned_leb128 (0); - } - - /* Record the offset in the pool at which this entry will - reside. */ - const auto offset_inserted - = (m_entry_pool_offsets.emplace (entry, m_entry_pool.size ()) - .second); - gdb_assert (offset_inserted); - - /* Write the entry to the pool, starting with the abbrev number. */ - m_entry_pool.append_unsigned_leb128 (abbrev); - - /* Unit index. */ - const auto it = m_cu_index_htab.find (entry->per_cu); - gdb_assert (it != m_cu_index_htab.cend ()); - m_entry_pool.append_unsigned_leb128 (it->second); - - /* DIE offset. */ - m_entry_pool.append_uint (dwarf5_offset_size (), - m_dwarf5_byte_order, - to_underlying (entry->die_offset)); + m_offsets_to_patch.emplace_back + (m_entry_pool.size (), parent); - /* Language. */ - m_entry_pool.append_unsigned_leb128 (entry->per_cu->dw_lang ()); - - /* Parent offset. */ - if (parent != nullptr) - { - m_offsets_to_patch.emplace_back (m_entry_pool.size (), parent); - - /* Write a dummy number, this gets patched later. */ - m_entry_pool.append_uint (4, m_dwarf5_byte_order, - 0xfafafafa); + /* Write a dummy number, this gets patched later. */ + m_entry_pool.append_uint (4, m_dwarf5_byte_order, + 0xfafafafa); + } } } @@ -1239,9 +1271,6 @@ write_cooked_index (cooked_index *table, for (const cooked_index_entry *entry : table->all_entries ()) { - const auto it = cu_index_htab.find (entry->per_cu); - gdb_assert (it != cu_index_htab.cend ()); - const char *name = entry->full_name (symtab->obstack ()); if (entry->lang == language_ada) @@ -1281,8 +1310,14 @@ write_cooked_index (cooked_index *table, else kind = GDB_INDEX_SYMBOL_KIND_OTHER; - symtab->add_index_entry (name, (entry->flags & IS_STATIC) != 0, - kind, it->second); + entry->visit_defining_cus ([&] (dwarf2_per_cu *per_cu) + { + const auto it = cu_index_htab.find (per_cu); + gdb_assert (it != cu_index_htab.cend ()); + symtab->add_index_entry (name, (entry->flags & IS_STATIC) != 0, + kind, it->second); + return true; + }); } } diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c index 141a6ea066f..afc276442bf 100644 --- a/gdb/dwarf2/read.c +++ b/gdb/dwarf2/read.c @@ -1642,6 +1642,11 @@ static struct compunit_symtab * dw2_instantiate_symtab (dwarf2_per_cu *per_cu, dwarf2_per_objfile *per_objfile, bool skip_partial) { + /* Expand the corresponding canonical outermost CU. This will + expand the desired CU as a side effect when following the + DW_TAG_imported_unit. */ + per_cu = per_cu->canonical_outermost_cu (); + if (!per_objfile->symtab_set_p (per_cu)) { free_cached_comp_units freer (per_objfile); @@ -3389,7 +3394,7 @@ cooked_index_worker_debug_info::process_unit if (this_cu->scanned.compare_exchange_strong (nope, true)) { gdb_assert (storage != nullptr); - cooked_indexer indexer (storage, this_cu, reader->cu ()->lang ()); + cooked_indexer indexer (storage, reader->cu ()->lang ()); indexer.make_index (reader); } } @@ -3409,7 +3414,7 @@ cooked_index_worker_debug_info::process_type_unit return; gdb_assert (storage != nullptr); - cooked_indexer indexer (storage, per_cu, cu->lang ()); + cooked_indexer indexer (storage, cu->lang ()); indexer.make_index (reader); } @@ -14187,15 +14192,15 @@ cooked_index_functions::search we see a symbol with an unknown language we find the CU's language. Only the .gdb_index reader creates such symbols. */ - enum language entry_lang = entry->lang; - if (lang_matcher != nullptr || entry_lang == language_unknown) + if (lang_matcher != nullptr || entry->lang == language_unknown) { entry->per_cu->ensure_lang (per_objfile); if (lang_matcher != nullptr && !entry->per_cu->maybe_multi_language () && !lang_matcher (entry->per_cu->lang ())) continue; - entry_lang = entry->per_cu->lang (); + else if (entry->lang == language_unknown) + entry->force_set_language (); } /* We've found the base name of the symbol; now walk its @@ -14204,7 +14209,7 @@ cooked_index_functions::search bool found = true; const cooked_index_entry *parent = entry->get_parent (); - const language_defn *lang_def = language_def (entry_lang); + const language_defn *lang_def = language_def (entry->lang); for (int i = name_vec.size () - 1; i > 0; --i) { /* If we ran out of entries, or if this segment doesn't @@ -14260,8 +14265,12 @@ cooked_index_functions::search else if (!symbol_matcher (full_name)) continue; - if (!dw2_search_one (entry->per_cu, per_objfile, cus_to_skip, - file_matcher, listener, nullptr)) + bool check = entry->visit_defining_cus ([&] (dwarf2_per_cu *per_cu) + { + return dw2_search_one (per_cu, per_objfile, cus_to_skip, + file_matcher, listener, nullptr); + }); + if (!check) return false; } } @@ -18024,6 +18033,30 @@ dwarf2_per_cu::ensure_lang (dwarf2_per_objfile *per_objfile) true, std::nullopt, abbrev_table_cache); } +/* See read.h. */ + +bool +dwarf2_per_cu::recursively_visit_cus (per_cu_callback callback) +{ + if (including_cus.empty ()) + return callback (this); + for (dwarf2_per_cu *iter : including_cus) + if (!iter->recursively_visit_cus (callback)) + return false; + return true; +} + +/* See read.h. */ + +dwarf2_per_cu * +dwarf2_per_cu::canonical_outermost_cu () +{ + dwarf2_per_cu *iter = this; + while (!iter->including_cus.empty ()) + iter = *iter->including_cus.begin (); + return iter; +} + /* Return the unit from ALL_UNITS that potentially contains TARGET. Since the unit lengths may not be known yet, this function doesn't check that diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h index 50bab220888..33ea33692cd 100644 --- a/gdb/dwarf2/read.h +++ b/gdb/dwarf2/read.h @@ -251,6 +251,15 @@ struct dwarf2_per_cu /* Backlink to the owner of this. */ dwarf2_per_bfd *m_per_bfd; + /* Compare two CUs. */ + struct compare + { + bool operator() (const dwarf2_per_cu *lhs, const dwarf2_per_cu *rhs) const + { + return lhs->index < rhs->index; + } + }; + public: /* The file and directory for this CU. This is cached so that we don't need to re-examine the DWO in some situations. This may be @@ -280,6 +289,13 @@ struct dwarf2_per_cu http://sourceware.org/bugzilla/show_bug.cgi?id=15021. */ std::vector imported_symtabs; + /* Pointer to all the CUs that include this CU via + DW_TAG_imported_unit. This is used for the special handling of + inline functions appearing in partial units. An ordered set is + used to ensure that the canonical CU is the same across + invocations of gdb. */ + std::set including_cus; + bool is_debug_types () const { return m_is_debug_types; } @@ -431,6 +447,27 @@ struct dwarf2_per_cu /* Free any cached file names. */ void free_cached_file_names (); + + /* Indicate that INCLUDER includes this CU via + DW_TAG_imported_unit. */ + void add_includer (dwarf2_per_cu *includer) + { + including_cus.emplace (includer); + } + + /* Type of callback used when visiting defining CUs. */ + using per_cu_callback = gdb::function_view; + + /* Calls CALLBACK for each CU that is the outermost includer of + ONE_CU. If ONE_CU has no includers, it calls CALLBACK on ONE_CU. + If any call to CALLBACK returns false, this immediately returns + false (skipping the remaining calls); otherwise returns true. */ + bool recursively_visit_cus (per_cu_callback callback); + + /* Return a canonical outermost CU corresponding to this CU. If + this CU is standalone (not included by other CUs), then this + method will simply return 'this'. */ + dwarf2_per_cu *canonical_outermost_cu (); }; /* Entry in the signatured_types hash table. */ -- 2.49.0