| /* DIE indexing |
| |
| Copyright (C) 2022-2025 Free Software Foundation, Inc. |
| |
| This file is part of GDB. |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program. If not, see <http://www.gnu.org/licenses/>. */ |
| |
| #ifndef GDB_DWARF2_COOKED_INDEX_H |
| #define GDB_DWARF2_COOKED_INDEX_H |
| |
| #include "dwarf2/cooked-index-entry.h" |
| #include "symtab.h" |
| #include "quick-symbol.h" |
| #include "addrmap.h" |
| #include "dwarf2/mapped-index.h" |
| #include "dwarf2/read.h" |
| #include "dwarf2/parent-map.h" |
| #include "gdbsupport/range-chain.h" |
| #include "dwarf2/cooked-index-shard.h" |
| #include "dwarf2/cooked-index-worker.h" |
| |
| /* The main index of DIEs. |
| |
| The index is created by multiple threads. The overall process is |
| somewhat complicated, so here's a diagram to help sort it out. |
| |
| The basic idea behind this design is (1) to do as much work as |
| possible in worker threads, and (2) to start the work as early as |
| possible. This combination should help hide the effort from the |
| user to the maximum possible degree. |
| |
| There are a number of different objects involved in this process. |
| Most of them are temporary -- they are created to handle different |
| phases of scanning, then discarded when possible. The "steady |
| state" objects are index itself (cooked_index, below), which holds |
| the entries (cooked_index_entry), and the implementation of the |
| "quick" API (e.g., cooked_index_functions, though there are |
| other variants). |
| |
| . Main Thread | Worker Threads |
| ============================================================ |
| . dwarf2_initialize_objfile |
| . | |
| . v |
| . cooked index ------------> cooked_index_worker::start |
| . | / | \ |
| . v / | \ |
| . install / | \ |
| . cooked_index_functions scan CUs in workers |
| . | create cooked_index_shard objects |
| . | \ | / |
| . v \|/ |
| . return to caller v |
| . initial scan is done |
| . state = MAIN_AVAILABLE |
| . "main" name now available |
| . | |
| . | |
| . if main thread calls... v |
| . compute_main_name cooked_index::set_contents |
| . | / | \ |
| . v / | \ |
| . wait (MAIN_AVAILABLE) finalization |
| . | \ | / |
| . v \ | / |
| . done state = FINALIZED |
| . | |
| . v |
| . maybe write to index cache |
| . state = CACHE_DONE |
| . ~cooked_index_worker |
| . |
| . |
| . if main thread calls... |
| . any other "quick" API |
| . | |
| . v |
| . wait (FINALIZED) |
| . | |
| . v |
| . use the index |
| */ |
| |
| class cooked_index : public dwarf_scanner_base |
| { |
| public: |
| cooked_index (cooked_index_worker_up &&worker); |
| ~cooked_index () override; |
| |
| DISABLE_COPY_AND_ASSIGN (cooked_index); |
| |
| /* Start reading the DWARF. */ |
| void start_reading () override; |
| |
| /* Called by cooked_index_worker to set the contents of this index |
| and transition to the MAIN_AVAILABLE state. */ |
| void set_contents (); |
| |
| /* A range over a vector of subranges. */ |
| using range = range_chain<cooked_index_shard::range>; |
| |
| /* Look up an entry by name. Returns a range of all matching |
| results. If COMPLETING is true, then a larger range, suitable |
| for completion, will be returned. */ |
| range find (const std::string &name, bool completing); |
| |
| /* Return a range of all the entries. */ |
| range all_entries () |
| { |
| wait (cooked_state::FINALIZED, true); |
| std::vector<cooked_index_shard::range> result_range; |
| result_range.reserve (m_shards.size ()); |
| for (auto &shard : m_shards) |
| result_range.push_back (shard->all_entries ()); |
| return range (std::move (result_range)); |
| } |
| |
| /* Look up ADDR in the address map, and return either the |
| corresponding CU, or nullptr if the address could not be |
| found. */ |
| dwarf2_per_cu *lookup (unrelocated_addr addr) override; |
| |
| /* Return a new vector of all the addrmaps used by all the indexes |
| held by this object. |
| |
| Elements of the vector may be nullptr. */ |
| std::vector<const addrmap *> get_addrmaps (); |
| |
| /* Return the entry that is believed to represent the program's |
| "main". This will return NULL if no such entry is available. */ |
| const cooked_index_entry *get_main () const; |
| |
| const char *get_main_name (struct obstack *obstack, enum language *lang) |
| const; |
| |
| cooked_index *index_for_writing () override |
| { |
| wait (cooked_state::FINALIZED, true); |
| return this; |
| } |
| |
| quick_symbol_functions_up make_quick_functions () const override; |
| |
| /* Dump a human-readable form of the contents of the index. */ |
| void dump (gdbarch *arch); |
| |
| /* Wait until this object reaches the desired state. Note that |
| DESIRED_STATE may not be INITIAL -- it does not make sense to |
| wait for this. If ALLOW_QUIT is true, timed waits will be done |
| and the quit flag will be checked in a loop. This may normally |
| only be called from the main thread; however, it is ok to call |
| from a worker as long as the desired state has already been |
| attained. (This property is needed by the index cache |
| writer.) */ |
| void wait (cooked_state desired_state, bool allow_quit = false); |
| |
| void wait_completely () override |
| { wait (cooked_state::CACHE_DONE); } |
| |
| private: |
| |
| /* The vector of cooked_index objects. This is stored because the |
| entries are stored on the obstacks in those objects. */ |
| std::vector<cooked_index_shard_up> m_shards; |
| |
| /* This tracks the current state. When this is nullptr, it means |
| that the state is CACHE_DONE -- it's important to note that only |
| the main thread may change the value of this pointer. */ |
| cooked_index_worker_up m_state; |
| }; |
| |
| /* An implementation of quick_symbol_functions for the cooked DWARF |
| index. */ |
| |
| struct cooked_index_functions : public dwarf2_base_index_functions |
| { |
| cooked_index *wait (struct objfile *objfile, bool allow_quit) |
| { |
| dwarf2_per_objfile *per_objfile = get_dwarf2_per_objfile (objfile); |
| cooked_index *table |
| = (gdb::checked_static_cast<cooked_index *> |
| (per_objfile->per_bfd->index_table.get ())); |
| table->wait (cooked_state::MAIN_AVAILABLE, allow_quit); |
| return table; |
| } |
| |
| struct compunit_symtab *find_compunit_symtab_by_address |
| (struct objfile *objfile, CORE_ADDR address) override; |
| |
| bool has_unexpanded_symtabs (struct objfile *objfile) override |
| { |
| wait (objfile, true); |
| return dwarf2_base_index_functions::has_unexpanded_symtabs (objfile); |
| } |
| |
| struct symtab *find_last_source_symtab (struct objfile *objfile) override |
| { |
| wait (objfile, true); |
| return dwarf2_base_index_functions::find_last_source_symtab (objfile); |
| } |
| |
| void forget_cached_source_info (struct objfile *objfile) override |
| { |
| wait (objfile, true); |
| dwarf2_base_index_functions::forget_cached_source_info (objfile); |
| } |
| |
| void print_stats (struct objfile *objfile, bool print_bcache) override |
| { |
| wait (objfile, true); |
| dwarf2_base_index_functions::print_stats (objfile, print_bcache); |
| } |
| |
| void dump (struct objfile *objfile) override |
| { |
| cooked_index *index = wait (objfile, true); |
| gdb_printf ("Cooked index in use:\n"); |
| gdb_printf ("\n"); |
| index->dump (objfile->arch ()); |
| } |
| |
| void expand_all_symtabs (struct objfile *objfile) override |
| { |
| wait (objfile, true); |
| dwarf2_base_index_functions::expand_all_symtabs (objfile); |
| } |
| |
| bool expand_symtabs_matching |
| (struct objfile *objfile, |
| expand_symtabs_file_matcher file_matcher, |
| const lookup_name_info *lookup_name, |
| expand_symtabs_symbol_matcher symbol_matcher, |
| expand_symtabs_expansion_listener expansion_notify, |
| block_search_flags search_flags, |
| domain_search_flags domain, |
| expand_symtabs_lang_matcher lang_matcher) override; |
| |
| struct compunit_symtab *find_pc_sect_compunit_symtab |
| (struct objfile *objfile, bound_minimal_symbol msymbol, |
| CORE_ADDR pc, struct obj_section *section, int warn_if_readin) override |
| { |
| wait (objfile, true); |
| return (dwarf2_base_index_functions::find_pc_sect_compunit_symtab |
| (objfile, msymbol, pc, section, warn_if_readin)); |
| } |
| |
| void map_symbol_filenames (objfile *objfile, symbol_filename_listener fun, |
| bool need_fullname) override |
| { |
| wait (objfile, true); |
| return (dwarf2_base_index_functions::map_symbol_filenames |
| (objfile, fun, need_fullname)); |
| } |
| |
| void compute_main_name (struct objfile *objfile) override |
| { |
| wait (objfile, false); |
| } |
| }; |
| |
| #endif /* GDB_DWARF2_COOKED_INDEX_H */ |