diff options
Diffstat (limited to 'gdb')
100 files changed, 2897 insertions, 2793 deletions
diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 8f6df5b..0c4102d 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -1097,7 +1097,9 @@ COMMON_SFILES = \ dwarf2/attribute.c \ dwarf2/comp-unit-head.c \ dwarf2/cooked-index.c \ - dwarf2/cooked-index-storage.c \ + dwarf2/cooked-index-entry.c \ + dwarf2/cooked-index-shard.c \ + dwarf2/cooked-index-worker.c \ dwarf2/cooked-indexer.c \ dwarf2/cu.c \ dwarf2/die.c \ @@ -1356,7 +1358,9 @@ HFILES_NO_SRCDIR = \ dummy-frame.h \ dwarf2/aranges.h \ dwarf2/cooked-index.h \ - dwarf2/cooked-index-storage.h \ + dwarf2/cooked-index-entry.h \ + dwarf2/cooked-index-shard.h \ + dwarf2/cooked-index-worker.h \ dwarf2/cooked-indexer.h \ dwarf2/cu.h \ dwarf2/frame-tailcall.h \ @@ -1912,8 +1916,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \ $(patsubst %.c,%.o,$(COMMON_SFILES)) \ $(SUBDIR_CLI_OBS) \ $(SUBDIR_MI_OBS) \ - $(SUBDIR_TARGET_OBS) \ - $(SUBDIR_GCC_COMPILE_OBS) + $(SUBDIR_TARGET_OBS) SUBDIRS = doc @subdirs@ data-directory CLEANDIRS = $(SUBDIRS) @@ -3,6 +3,10 @@ *** Changes since GDB 16 +* Debugger Adapter Protocol changes + + ** GDB now supports the "completions" request. + * "set style" commands now supports numeric format for basic colors from 0 to 255 and #RRGGBB format for TrueColor. @@ -108,6 +112,10 @@ qXfer:threads:read * Support for stabs debugging format and the a.out/dbx object format is deprecated, and will be removed in GDB 18. +* A new configure option was added, allowing support for the compile + subsystem to be disabled at configure time, in the form of + --disable-gdb-compile. + *** Changes in GDB 16 * Support for Nios II targets has been removed as this architecture @@ -442,6 +442,9 @@ more obscure GDB `configure' options are not listed here. Requires a curses library (ncurses and cursesX are also supported). +`--disable-gdb-compile' + Build GDB without support for the 'compile' command. + `--with-curses' Use the curses library instead of the termcap library, for text-mode terminal operations. diff --git a/gdb/aarch64-linux-tdep.c b/gdb/aarch64-linux-tdep.c index 0b4ae7f..c825699 100644 --- a/gdb/aarch64-linux-tdep.c +++ b/gdb/aarch64-linux-tdep.c @@ -2275,7 +2275,7 @@ aarch64_canonicalize_syscall (enum aarch64_syscall syscall_number) SYSCALL_MAP (clone); SYSCALL_MAP (execve); - SYSCALL_MAP_RENAME (mmap, gdb_sys_mmap2); + SYSCALL_MAP_RENAME (mmap, gdb_sys_old_mmap); SYSCALL_MAP (fadvise64); SYSCALL_MAP (swapon); diff --git a/gdb/addrmap.c b/gdb/addrmap.c index 9eb330b..1fc95f3 100644 --- a/gdb/addrmap.c +++ b/gdb/addrmap.c @@ -178,6 +178,29 @@ addrmap_mutable::force_transition (CORE_ADDR addr) } +/* Compare keys as CORE_ADDR * values. */ +static int +splay_compare_CORE_ADDR_ptr (splay_tree_key ak, splay_tree_key bk) +{ + CORE_ADDR a = * (CORE_ADDR *) ak; + CORE_ADDR b = * (CORE_ADDR *) bk; + + /* We can't just return a-b here, because of over/underflow. */ + if (a < b) + return -1; + else if (a == b) + return 0; + else + return 1; +} + + +static void +xfree_wrapper (splay_tree_key key) +{ + xfree ((void *) key); +} + void addrmap_mutable::set_empty (CORE_ADDR start, CORE_ADDR end_inclusive, void *obj) @@ -185,6 +208,10 @@ addrmap_mutable::set_empty (CORE_ADDR start, CORE_ADDR end_inclusive, splay_tree_node n, next; void *prior_value; + if (tree == nullptr) + tree = splay_tree_new (splay_compare_CORE_ADDR_ptr, xfree_wrapper, + nullptr /* no delete value */); + /* If we're being asked to set all empty portions of the given address range to empty, then probably the caller is confused. (If that turns out to be useful in some cases, then we can change @@ -233,6 +260,9 @@ addrmap_mutable::set_empty (CORE_ADDR start, CORE_ADDR end_inclusive, void * addrmap_mutable::do_find (CORE_ADDR addr) const { + if (tree == nullptr) + return nullptr; + splay_tree_node n = splay_tree_lookup (addr); if (n != nullptr) { @@ -287,16 +317,6 @@ addrmap_fixed::addrmap_fixed (struct obstack *obstack, gdb_assert (num_transitions == transition_count); } - -void -addrmap_mutable::relocate (CORE_ADDR offset) -{ - /* Not needed yet. */ - internal_error (_("addrmap_relocate is not implemented yet " - "for mutable addrmaps")); -} - - /* This is a splay_tree_foreach_fn. */ static int @@ -311,43 +331,20 @@ addrmap_mutable_foreach_worker (splay_tree_node node, void *data) int addrmap_mutable::do_foreach (addrmap_foreach_fn fn) const { - return splay_tree_foreach (tree, addrmap_mutable_foreach_worker, &fn); -} - - -/* Compare keys as CORE_ADDR * values. */ -static int -splay_compare_CORE_ADDR_ptr (splay_tree_key ak, splay_tree_key bk) -{ - CORE_ADDR a = * (CORE_ADDR *) ak; - CORE_ADDR b = * (CORE_ADDR *) bk; - - /* We can't just return a-b here, because of over/underflow. */ - if (a < b) - return -1; - else if (a == b) + if (tree == nullptr) return 0; - else - return 1; -} - - -static void -xfree_wrapper (splay_tree_key key) -{ - xfree ((void *) key); + return splay_tree_foreach (tree, addrmap_mutable_foreach_worker, &fn); } -addrmap_mutable::addrmap_mutable () - : tree (splay_tree_new (splay_compare_CORE_ADDR_ptr, xfree_wrapper, - nullptr /* no delete value */)) -{ -} -addrmap_mutable::~addrmap_mutable () +void +addrmap_mutable::clear () { if (tree != nullptr) - splay_tree_delete (tree); + { + splay_tree_delete (tree); + tree = nullptr; + } } @@ -443,7 +440,7 @@ test_addrmap () CHECK_ADDRMAP_FIND (map, array, 13, 19, nullptr); /* Create corresponding fixed addrmap. */ - struct addrmap *map2 + addrmap_fixed *map2 = new (&temp_obstack) addrmap_fixed (&temp_obstack, map.get ()); SELF_CHECK (map2 != nullptr); CHECK_ADDRMAP_FIND (map2, array, 0, 9, nullptr); diff --git a/gdb/addrmap.h b/gdb/addrmap.h index 95f6ec8..a2feb68 100644 --- a/gdb/addrmap.h +++ b/gdb/addrmap.h @@ -52,10 +52,6 @@ struct addrmap void *find (CORE_ADDR addr) { return this->do_find (addr); } - /* Relocate all the addresses in MAP by OFFSET. (This can be applied - to either mutable or immutable maps.) */ - virtual void relocate (CORE_ADDR offset) = 0; - /* Call FN for every address in MAP, following an in-order traversal. If FN ever returns a non-zero value, the iteration ceases immediately, and the value is returned. Otherwise, this function @@ -94,7 +90,8 @@ public: addrmap_fixed (addrmap_fixed &&other) = default; addrmap_fixed &operator= (addrmap_fixed &&) = default; - void relocate (CORE_ADDR offset) override; + /* Relocate all the addresses in this map by OFFSET. */ + void relocate (CORE_ADDR offset); private: void *do_find (CORE_ADDR addr) const override; @@ -126,8 +123,12 @@ struct addrmap_mutable final : public addrmap { public: - addrmap_mutable (); - ~addrmap_mutable (); + addrmap_mutable () = default; + ~addrmap_mutable () + { + clear (); + } + DISABLE_COPY_AND_ASSIGN (addrmap_mutable); addrmap_mutable (addrmap_mutable &&other) @@ -138,7 +139,13 @@ public: addrmap_mutable &operator= (addrmap_mutable &&other) { - std::swap (tree, other.tree); + /* Handle self-move. */ + if (this != &other) + { + clear (); + tree = other.tree; + other.tree = nullptr; + } return *this; } @@ -181,7 +188,9 @@ public: representation. */ void set_empty (CORE_ADDR start, CORE_ADDR end_inclusive, void *obj); - void relocate (CORE_ADDR offset) override; + + /* Clear this addrmap. */ + void clear (); private: void *do_find (CORE_ADDR addr) const override; @@ -204,7 +213,7 @@ private: function, we can't keep a freelist for keys. Since mutable addrmaps are only used temporarily right now, we just leak keys from deleted nodes; they'll be freed when the obstack is freed. */ - splay_tree tree; + splay_tree tree = nullptr; /* Various helper methods. */ splay_tree_key allocate_key (CORE_ADDR addr); diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c index e290d2c..a7868c3 100644 --- a/gdb/amd64-linux-tdep.c +++ b/gdb/amd64-linux-tdep.c @@ -412,7 +412,7 @@ amd64_canonicalize_syscall (enum amd64_syscall syscall_number) case amd64_sys_mmap: case amd64_x32_sys_mmap: - return gdb_sys_mmap2; + return gdb_sys_old_mmap; case amd64_sys_mprotect: case amd64_x32_sys_mprotect: diff --git a/gdb/arm-linux-tdep.c b/gdb/arm-linux-tdep.c index 858705e..53c8a05 100644 --- a/gdb/arm-linux-tdep.c +++ b/gdb/arm-linux-tdep.c @@ -1361,8 +1361,8 @@ arm_canonicalize_syscall (int syscall) case 86: return gdb_sys_uselib; case 87: return gdb_sys_swapon; case 88: return gdb_sys_reboot; - case 89: return gdb_old_readdir; - case 90: return gdb_old_mmap; + case 89: return gdb_sys_old_readdir; + case 90: return gdb_sys_old_mmap; case 91: return gdb_sys_munmap; case 92: return gdb_sys_truncate; case 93: return gdb_sys_ftruncate; diff --git a/gdb/arm-pikeos-tdep.c b/gdb/arm-pikeos-tdep.c index 4760755..b2c93bd 100644 --- a/gdb/arm-pikeos-tdep.c +++ b/gdb/arm-pikeos-tdep.c @@ -36,8 +36,6 @@ arm_pikeos_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) static enum gdb_osabi arm_pikeos_osabi_sniffer (bfd *abfd) { - long number_of_symbols; - long i; int pikeos_stack_found = 0; int pikeos_stack_size_found = 0; @@ -50,20 +48,15 @@ arm_pikeos_osabi_sniffer (bfd *abfd) OS ABI sniffers are called before the minimal symtabs are created. So inspect the symbol table using BFD. */ - long storage_needed = bfd_get_symtab_upper_bound (abfd); - if (storage_needed <= 0) - return GDB_OSABI_UNKNOWN; - - gdb::unique_xmalloc_ptr<asymbol *> symbol_table - ((asymbol **) xmalloc (storage_needed)); - number_of_symbols = bfd_canonicalize_symtab (abfd, symbol_table.get ()); + gdb::array_view<asymbol *> symbol_table + = gdb_bfd_canonicalize_symtab (abfd, false); - if (number_of_symbols <= 0) + if (symbol_table.empty ()) return GDB_OSABI_UNKNOWN; - for (i = 0; i < number_of_symbols; i++) + for (const asymbol *sym : symbol_table) { - const char *name = bfd_asymbol_name (symbol_table.get ()[i]); + const char *name = bfd_asymbol_name (sym); if (strcmp (name, "_vm_stack") == 0 || strcmp (name, "__p4_stack") == 0) diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c index 3085ca1..0fb6fd9 100644 --- a/gdb/breakpoint.c +++ b/gdb/breakpoint.c @@ -1535,6 +1535,11 @@ void breakpoint_set_commands (struct breakpoint *b, counted_command_line &&commands) { + /* If the commands have not changed then there's no need to update + anything, and no need to emit a breakpoint modified event. */ + if (commands_equal (b->commands.get (), commands.get ())) + return; + validate_commands_for_breakpoint (b, commands.get ()); b->commands = std::move (commands); @@ -3099,7 +3104,6 @@ insert_bp_location (struct bp_location *bl, || shared_objfile_contains_address_p (bl->pspace, bl->address))) { - /* See also: disable_breakpoints_in_shlibs. */ bl->shlib_disabled = 1; notify_breakpoint_modified (bl->owner); if (!*disabled_breaks) @@ -8079,44 +8083,19 @@ create_and_insert_solib_event_breakpoint (struct gdbarch *gdbarch, CORE_ADDR add return b; } -/* See breakpoint.h. */ - -void -disable_breakpoints_in_shlibs (program_space *pspace) -{ - for (bp_location *loc : all_bp_locations ()) - { - /* ALL_BP_LOCATIONS bp_location has LOC->OWNER always non-NULL. */ - struct breakpoint *b = loc->owner; - - /* We apply the check to all breakpoints, including disabled for - those with loc->duplicate set. This is so that when breakpoint - becomes enabled, or the duplicate is removed, gdb will try to - insert all breakpoints. If we don't set shlib_disabled here, - we'll try to insert those breakpoints and fail. */ - if (((b->type == bp_jit_event) - || is_breakpoint (b) - || is_tracepoint (b)) - && loc->pspace == pspace - && !loc->shlib_disabled - && solib_name_from_address (loc->pspace, loc->address) - ) - { - loc->shlib_disabled = 1; - } - } -} - /* Disable any breakpoints and tracepoints that are in SOLIB upon notification of unloaded_shlib. Only apply to enabled breakpoints, disabled ones can just stay disabled. When STILL_IN_USE is true, SOLIB hasn't really been unmapped from - the inferior. In this case, don't disable anything. */ + the inferior. In this case, don't disable anything. + + When SILENT is false notify the user if any breakpoints are disabled, + otherwise, still disable the breakpoints, but don't tell the user. */ static void disable_breakpoints_in_unloaded_shlib (program_space *pspace, const solib &solib, - bool still_in_use) + bool still_in_use, bool silent) { if (still_in_use) return; @@ -8160,7 +8139,7 @@ disable_breakpoints_in_unloaded_shlib (program_space *pspace, const solib &solib bp_modified = true; - if (!disabled_shlib_breaks && user_breakpoint_p (&b)) + if (!disabled_shlib_breaks && !silent && user_breakpoint_p (&b)) { target_terminal::ours_for_output (); warning (_("Temporarily disabling breakpoints " diff --git a/gdb/bsd-uthread.c b/gdb/bsd-uthread.c index 67db0ca..129e7a6 100644 --- a/gdb/bsd-uthread.c +++ b/gdb/bsd-uthread.c @@ -295,7 +295,7 @@ bsd_uthread_solib_loaded (solib &so) static void bsd_uthread_solib_unloaded (program_space *pspace, const solib &so, - bool still_in_use) + bool still_in_use, bool /* silent */) { if (bsd_uthread_solib_name.empty () || still_in_use) return; diff --git a/gdb/c-lang.c b/gdb/c-lang.c index c28493f..5592234 100644 --- a/gdb/c-lang.c +++ b/gdb/c-lang.c @@ -807,22 +807,6 @@ public: } /* See language.h. */ - std::unique_ptr<compile_instance> get_compile_instance () const override - { - return c_get_compile_context (); - } - - /* See language.h. */ - std::string compute_program (compile_instance *inst, - const char *input, - struct gdbarch *gdbarch, - const struct block *expr_block, - CORE_ADDR expr_pc) const override - { - return c_compute_program (inst, input, gdbarch, expr_block, expr_pc); - } - - /* See language.h. */ bool can_print_type_offsets () const override { @@ -943,22 +927,6 @@ public: } /* See language.h. */ - std::unique_ptr<compile_instance> get_compile_instance () const override - { - return cplus_get_compile_context (); - } - - /* See language.h. */ - std::string compute_program (compile_instance *inst, - const char *input, - struct gdbarch *gdbarch, - const struct block *expr_block, - CORE_ADDR expr_pc) const override - { - return cplus_compute_program (inst, input, gdbarch, expr_block, expr_pc); - } - - /* See language.h. */ unsigned int search_name_hash (const char *name) const override { return cp_search_name_hash (name); diff --git a/gdb/c-lang.h b/gdb/c-lang.h index 0e733d8..06b7ad0 100644 --- a/gdb/c-lang.h +++ b/gdb/c-lang.h @@ -25,7 +25,6 @@ struct ui_file; struct language_arch_info; struct type_print_options; struct parser_state; -struct compile_instance; #include "compile/compile.h" #include "value.h" @@ -132,43 +131,6 @@ extern bool c_is_string_type_p (struct type *type); extern int c_textual_element_type (struct type *, char); -/* Create a new instance of the C compiler and return it. This - function never returns NULL, but rather throws an exception on - failure. This is suitable for use as the - language_defn::get_compile_instance method. */ - -extern std::unique_ptr<compile_instance> c_get_compile_context (); - -/* Create a new instance of the C++ compiler and return it. This - function never returns NULL, but rather throws an exception on - failure. This is suitable for use as the - language_defn::get_compile_instance method. */ - -extern std::unique_ptr<compile_instance> cplus_get_compile_context (); - -/* This takes the user-supplied text and returns a new bit of code to - compile. - - This is used as the compute_program language method; see that - for a description of the arguments. */ - -extern std::string c_compute_program (compile_instance *inst, - const char *input, - struct gdbarch *gdbarch, - const struct block *expr_block, - CORE_ADDR expr_pc); - -/* This takes the user-supplied text and returns a new bit of code to compile. - - This is used as the compute_program language method; see that - for a description of the arguments. */ - -extern std::string cplus_compute_program (compile_instance *inst, - const char *input, - struct gdbarch *gdbarch, - const struct block *expr_block, - CORE_ADDR expr_pc); - /* Return the canonical form of the C symbol NAME. If NAME is already canonical, return nullptr. */ diff --git a/gdb/cli/cli-script.c b/gdb/cli/cli-script.c index 5decf3b..0337d01 100644 --- a/gdb/cli/cli-script.c +++ b/gdb/cli/cli-script.c @@ -660,9 +660,13 @@ execute_control_command_1 (struct command_line *cmd, int from_tty) } case compile_control: +#if defined(HAVE_COMPILE) eval_compile_command (cmd, NULL, cmd->control_u.compile.scope, cmd->control_u.compile.scope_data); ret = simple_control; +#else + error (_("compile support has not been compiled into gdb")); +#endif break; case define_control: @@ -1621,6 +1625,65 @@ define_prefix_command (const char *comname, int from_tty) c->allow_unknown = c->user_commands.get () != nullptr; } +/* See cli/cli-script.h. */ + +bool +commands_equal (const command_line *a, const command_line *b) +{ + if ((a == nullptr) != (b == nullptr)) + return false; + + while (a != nullptr) + { + /* We are either at the end of both command lists, or there's + another command in both lists. */ + if ((a->next == nullptr) != (b->next == nullptr)) + return false; + + /* There's a command line for both, or neither. */ + if ((a->line == nullptr) != (b->line == nullptr)) + return false; + + /* Check control_type matches. */ + if (a->control_type != b->control_type) + return false; + + if (a->control_type == compile_control) + { + if (a->control_u.compile.scope != b->control_u.compile.scope) + return false; + + /* This is where we "fail safe". The scope_data is a 'void *' + pointer which changes in meaning based on the value of + 'scope'. It is possible that two different 'void *' pointers + could point to the equal scope data, however, we just assume + that if the pointers are different, then the scope_data is + different. This could be improved in the future. */ + if (a->control_u.compile.scope_data + != b->control_u.compile.scope_data) + return false; + } + + /* Check lines are identical. */ + if (a->line != nullptr && strcmp (a->line, b->line) != 0) + return false; + + /* Check body_list_0. */ + if (!commands_equal (a->body_list_0.get (), b->body_list_0.get ())) + return false; + + /* Check body_list_1. */ + if (!commands_equal (a->body_list_1.get (), b->body_list_1.get ())) + return false; + + /* Move to the next element in each chain. */ + a = a->next; + b = b->next; + } + + return true; +} + /* Used to implement source_command. */ diff --git a/gdb/cli/cli-script.h b/gdb/cli/cli-script.h index df7316e..23a1e1f 100644 --- a/gdb/cli/cli-script.h +++ b/gdb/cli/cli-script.h @@ -184,4 +184,14 @@ extern void print_command_trace (const char *cmd, ...) extern void reset_command_nest_depth (void); +/* Return true if A and B are identical. Some commands carry around a + 'void *' compilation context, in this case this function doesn't try to + validate if the context is actually the same or not, and will just + return false indicating the commands have changed. That is, a return + value of true is a guarantee that the commands are equal, a return + value of false means the commands are possibly different (and in most + cases are different). */ + +extern bool commands_equal (const command_line *a, const command_line *b); + #endif /* GDB_CLI_CLI_SCRIPT_H */ diff --git a/gdb/compile/compile-internal.h b/gdb/compile/compile-internal.h index f4cc9ee..789782d 100644 --- a/gdb/compile/compile-internal.h +++ b/gdb/compile/compile-internal.h @@ -80,4 +80,43 @@ private: std::string m_object_file; }; +struct compile_instance; + +/* Create a new instance of the C compiler and return it. This + function never returns NULL, but rather throws an exception on + failure. This is suitable for use as the + language_defn::get_compile_instance method. */ + +extern std::unique_ptr<compile_instance> c_get_compile_context (); + +/* Create a new instance of the C++ compiler and return it. This + function never returns NULL, but rather throws an exception on + failure. This is suitable for use as the + language_defn::get_compile_instance method. */ + +extern std::unique_ptr<compile_instance> cplus_get_compile_context (); + +/* This takes the user-supplied text and returns a new bit of code to + compile. + + This is used as the compute_program language method; see that + for a description of the arguments. */ + +extern std::string c_compute_program (compile_instance *inst, + const char *input, + struct gdbarch *gdbarch, + const struct block *expr_block, + CORE_ADDR expr_pc); + +/* This takes the user-supplied text and returns a new bit of code to compile. + + This is used as the compute_program language method; see that + for a description of the arguments. */ + +extern std::string cplus_compute_program (compile_instance *inst, + const char *input, + struct gdbarch *gdbarch, + const struct block *expr_block, + CORE_ADDR expr_pc); + #endif /* GDB_COMPILE_COMPILE_INTERNAL_H */ diff --git a/gdb/compile/compile-object-load.c b/gdb/compile/compile-object-load.c index ef77ee3..05e5b43 100644 --- a/gdb/compile/compile-object-load.c +++ b/gdb/compile/compile-object-load.c @@ -605,9 +605,7 @@ compile_object_load (const compile_file_names &file_names, CORE_ADDR regs_addr, out_value_addr = 0; struct symbol *func_sym; struct type *func_type; - long storage_needed; - asymbol **symbol_table, **symp; - long number_of_symbols, missing_symbols; + long missing_symbols; struct type *regs_type, *out_value_type = NULL; char **matching; struct objfile *objfile; @@ -635,11 +633,6 @@ compile_object_load (const compile_file_names &file_names, setup_sections_data.setup_one_section (sect); setup_sections_data.setup_one_section (nullptr); - storage_needed = bfd_get_symtab_upper_bound (abfd.get ()); - if (storage_needed < 0) - error (_("Cannot read symbols of compiled module \"%s\": %s"), - filename.get (), bfd_errmsg (bfd_get_error ())); - /* SYMFILE_VERBOSE is not passed even if FROM_TTY, user is not interested in "Reading symbols from ..." message for automatically generated file. */ scoped_objfile_unlinker objfile_holder (symbol_file_add_from_bfd @@ -692,21 +685,12 @@ compile_object_load (const compile_file_names &file_names, "module \"%s\"."), GCC_FE_WRAPPER_FUNCTION, objfile_name (objfile)); - /* The memory may be later needed - by bfd_generic_get_relocated_section_contents - called from default_symfile_relocate. */ - symbol_table = (asymbol **) obstack_alloc (&objfile->objfile_obstack, - storage_needed); - number_of_symbols = bfd_canonicalize_symtab (abfd.get (), symbol_table); - if (number_of_symbols < 0) - error (_("Cannot parse symbols of compiled module \"%s\": %s"), - filename.get (), bfd_errmsg (bfd_get_error ())); + gdb::array_view<asymbol *> symbol_table + = gdb_bfd_canonicalize_symtab (abfd.get ()); missing_symbols = 0; - for (symp = symbol_table; symp < symbol_table + number_of_symbols; symp++) + for (asymbol *sym : symbol_table) { - asymbol *sym = *symp; - if (sym->flags != 0) continue; sym->flags = BSF_GLOBAL; @@ -800,7 +784,7 @@ compile_object_load (const compile_file_names &file_names, if (missing_symbols) error (_("%ld symbols were missing, cannot continue."), missing_symbols); - bfd_map_over_sections (abfd.get (), copy_sections, symbol_table); + bfd_map_over_sections (abfd.get (), copy_sections, symbol_table.data ()); regs_type = get_regs_type (func_sym, objfile); if (regs_type == NULL) diff --git a/gdb/compile/compile.c b/gdb/compile/compile.c index d6bcc1f..01f43ad 100644 --- a/gdb/compile/compile.c +++ b/gdb/compile/compile.c @@ -46,16 +46,17 @@ #include "gdbsupport/scoped_ignore_signal.h" #include "gdbsupport/buildargv.h" +/* Hold "compile" commands. */ + +static struct cmd_list_element *compile_command_list; + +#ifdef HAVE_COMPILE /* Initial filename for temporary files. */ #define TMP_PREFIX "/tmp/gdbobj-" -/* Hold "compile" commands. */ - -static struct cmd_list_element *compile_command_list; - /* Debug flag for "compile" commands. */ bool compile_debug; @@ -527,6 +528,41 @@ print_callback (void *ignore, const char *message) gdb_puts (message, gdb_stderr); } +/* Helper for compile_to_object, to find the compile context + based on the current language. */ +static std::unique_ptr<compile_instance> +get_language_compile_context () +{ + switch (current_language->la_language) + { + case language_c: + return c_get_compile_context (); + case language_cplus: + return cplus_get_compile_context (); + default: + return {}; + } +} + +/* Helper for compile_to_object, to call the correct + compute_program based on the current language. */ +static std::string +compute_program_language (compile_instance *inst, const char *input, + struct gdbarch *gdbarch, + const struct block *block, + CORE_ADDR pc) +{ + switch (current_language->la_language) + { + case language_c: + return c_compute_program (inst, input, gdbarch, block, pc); + case language_cplus: + return cplus_compute_program (inst, input, gdbarch, block, pc); + default: + gdb_assert_not_reached ("Unsupported language"); + } +} + /* Process the compilation request. On success it returns the object and source file names. On an error condition, error () is called. */ @@ -550,7 +586,8 @@ compile_to_object (struct command_line *cmd, const char *cmd_string, /* Set up instance and context for the compiler. */ std::unique_ptr<compile_instance> compiler - = current_language->get_compile_instance (); + = get_language_compile_context (); + if (compiler == nullptr) error (_("No compiler support for language %s."), current_language->name ()); @@ -582,8 +619,8 @@ compile_to_object (struct command_line *cmd, const char *cmd_string, error (_("Neither a simple expression, or a multi-line specified.")); std::string code - = current_language->compute_program (compiler.get (), input, gdbarch, - expr_block, expr_pc); + = compute_program_language (compiler.get (), input, gdbarch, + expr_block, expr_pc); if (compile_debug) gdb_printf (gdb_stdlog, "debug output:\n\n%s", code.c_str ()); @@ -816,6 +853,18 @@ compile_instance::compile (const char *filename, int verbose_level) #undef FORWARD +#else /* HAVE_COMPILE */ + +/* The "compile" prefix command, when support was disabled. */ + +static void +compile_command (const char *args, int from_tty) +{ + error (_("This command is not supported.")); +} + +#endif /* HAVE_COMPILE */ + /* See compile.h. */ cmd_list_element *compile_cmd_element = nullptr; @@ -823,14 +872,25 @@ void _initialize_compile (); void _initialize_compile () { - struct cmd_list_element *c = NULL; - compile_cmd_element = add_prefix_cmd ("compile", class_obscure, - compile_command, _("\ + compile_command, +#ifdef HAVE_COMPILE + _("\ Command to compile source code and inject it into the inferior."), +#else /* HAVE_COMPILE */ + _("\ +Command to compile source code and inject it into the inferior.\n\ +\n\ +Code compilation and injection is not supported in this copy of GDB.\n\ +This command is only a placeholder."), +#endif /* HAVE_COMPILE */ &compile_command_list, 1, &cmdlist); add_com_alias ("expression", compile_cmd_element, class_obscure, 0); +#ifdef HAVE_COMPILE + + struct cmd_list_element *c = NULL; + const auto compile_opts = make_compile_options_def_group (nullptr); static const std::string compile_code_help @@ -937,4 +997,5 @@ It should be absolute filename of the gcc executable.\n\ If empty the default target triplet will be searched in $PATH."), NULL, show_compile_gcc, &setlist, &showlist); +#endif /* HAVE_COMPILE */ } diff --git a/gdb/config.in b/gdb/config.in index db63aea..86ff67d 100644 --- a/gdb/config.in +++ b/gdb/config.in @@ -104,6 +104,9 @@ the CoreFoundation framework. */ #undef HAVE_CFPREFERENCESCOPYAPPVALUE +/* Define if compiling support to gdb compile. */ +#undef HAVE_COMPILE + /* Define to 1 if you have the <cursesX.h> header file. */ #undef HAVE_CURSESX_H diff --git a/gdb/configure b/gdb/configure index 3080413..e8a649f 100755 --- a/gdb/configure +++ b/gdb/configure @@ -956,6 +956,7 @@ with_libexpat_type with_python with_python_libdir with_guile +enable_gdb_compile enable_source_highlight with_sysroot with_system_gdbinit @@ -1650,6 +1651,8 @@ Optional Features: --enable-gdbtk enable gdbtk graphical user interface (GUI) --enable-profiling enable profiling of GDB --enable-codesign=CERT sign gdb with 'codesign -s CERT' + --enable-gdb-compile enable support for the compile subsystem, default + 'yes' --enable-source-highlight enable source-highlight for source listings --enable-werror treat compile warnings as errors @@ -11500,7 +11503,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 11503 "configure" +#line 11506 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -11606,7 +11609,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 11609 "configure" +#line 11612 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -28969,6 +28972,38 @@ fi # ---------------------------- # +# Check for compile support. # +# ---------------------------- # + +# Check whether --enable-gdb-compile was given. +if test "${enable_gdb_compile+set}" = set; then : + enableval=$enable_gdb_compile; + case $enableval in + yes | no) + ;; + *) + as_fn_error $? "bad value $enableval for --enable-gdb-compile" "$LINENO" 5 + ;; + esac + +else + enable_gdb_compile=yes +fi + + +if test "${enable_gdb_compile}" == yes; then + +$as_echo "#define HAVE_COMPILE 1" >>confdefs.h + + CONFIG_OBS="$CONFIG_OBS \$(SUBDIR_GCC_COMPILE_OBS)" +else + # Even if compile support is not enabled, we need this file to define + # the "compile" command. + CONFIG_OBS="$CONFIG_OBS compile/compile.o" + CONFIG_SRCS="$CONFIG_SRCS compile/compile.c" +fi + +# ---------------------------- # # Check for source highlight. # # ---------------------------- # diff --git a/gdb/configure.ac b/gdb/configure.ac index eafbf5a..2411b10 100644 --- a/gdb/configure.ac +++ b/gdb/configure.ac @@ -1222,6 +1222,26 @@ AC_SUBST(GUILE_LIBS) AM_CONDITIONAL(HAVE_GUILE, test "${have_libguile}" != no) # ---------------------------- # +# Check for compile support. # +# ---------------------------- # + +AC_ARG_ENABLE([gdb-compile], + AS_HELP_STRING([--enable-gdb-compile], + [enable support for the compile subsystem, default 'yes']), + [GDB_CHECK_YES_NO_VAL([$enableval], [--enable-gdb-compile])], + [enable_gdb_compile=yes]) + +if test "${enable_gdb_compile}" == yes; then + AC_DEFINE(HAVE_COMPILE, 1, [Define if compiling support to gdb compile.]) + CONFIG_OBS="$CONFIG_OBS \$(SUBDIR_GCC_COMPILE_OBS)" +else + # Even if compile support is not enabled, we need this file to define + # the "compile" command. + CONFIG_OBS="$CONFIG_OBS compile/compile.o" + CONFIG_SRCS="$CONFIG_SRCS compile/compile.c" +fi + +# ---------------------------- # # Check for source highlight. # # ---------------------------- # diff --git a/gdb/contrib/codespell-ignore-words.txt b/gdb/contrib/codespell-ignore-words.txt index a8287f7..2d6e13a 100644 --- a/gdb/contrib/codespell-ignore-words.txt +++ b/gdb/contrib/codespell-ignore-words.txt @@ -1,3 +1,2 @@ configury -invokable -useable +SME diff --git a/gdb/contrib/common-misspellings.txt b/gdb/contrib/common-misspellings.txt deleted file mode 100644 index 5772f66..0000000 --- a/gdb/contrib/common-misspellings.txt +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (C) 2024 Free Software Foundation, Inc. -# 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/>. - -# This file contains additions to and overrides for -# wikipedia-common-misspellings.txt. - -# Common spelling mistakes. - -inbetween->between, in between, in-between -sofar->so far -doens't->doesn't -behavour->behavior -behaviour->behavior -arrithemetic->arithmetic -electricaly->electrically - -# Identity rules. - -thru->thru diff --git a/gdb/contrib/setup.cfg b/gdb/contrib/setup.cfg new file mode 100644 index 0000000..dbff165 --- /dev/null +++ b/gdb/contrib/setup.cfg @@ -0,0 +1,6 @@ +[codespell] + +# Skip ChangeLogs and generated files. +skip = */ChangeLog*,*/configure,gdbsupport/Makefile.in,*.dat,*.eps,gdb/features/*.c,gdb/ada-casefold.h,gdb/copying.c,gdb/gdbarch-gen.h,gdb/gdbarch-gen.c,gdb/target-delegates-gen.c + +ignore-words = gdb/contrib/codespell-ignore-words.txt diff --git a/gdb/contrib/spellcheck.sh b/gdb/contrib/spellcheck.sh deleted file mode 100755 index 420891f..0000000 --- a/gdb/contrib/spellcheck.sh +++ /dev/null @@ -1,536 +0,0 @@ -#!/bin/bash - -# Copyright (C) 2024 Free Software Foundation, Inc. -# 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/>. - -# Script to auto-correct common spelling mistakes. -# -# Example usage: -# $ ./gdb/contrib/spellcheck.sh gdb* - -scriptdir=$(cd "$(dirname "$0")" || exit; pwd -P) -this_script=$scriptdir/$(basename "$0") - -url=https://en.wikipedia.org/wiki/Wikipedia:Lists_of_common_misspellings/For_machines -cache_dir=$scriptdir/../../.git -cache_file=wikipedia-common-misspellings.txt -dictionary=$cache_dir/$cache_file -local_dictionary=$scriptdir/common-misspellings.txt -cache_file2=spell-check.pat1 - -bash_version_at_least () -{ - local major - major="$1" - local minor - minor="$2" - - if [ "$bash_major" = "" ]; then - bash_major=$(echo "$BASH_VERSION" | awk -F '.' '{print $1}') - bash_minor=$(echo "$BASH_VERSION" | awk -F '.' '{print $2}') - fi - - if [ "$bash_major" -lt "$major" ]; then - # Major version less then required, return false. - return 1 - fi - - if [ "$bash_major" -gt "$major" ]; then - # Major version more then required, return true. - return 0 - fi - - # Check minor version. - [ "$bash_minor" -ge "$minor" ] -} - -# Separators: space, slash, tab, colon, comma. -declare -a grep_separators -grep_separators=( - " " - "/" - " " - ":" - "," - "\"" -) -declare -a sed_separators -sed_separators=( - " " - "/" - "\t" - ":" - "," - "\"" -) - -# Pre: start of line, left parenthesis. -declare -a grep_pre -grep_pre=( - "^" - "\(" -) -declare -a sed_pre -sed_pre=( - "^" - "(" -) - -# Post: dot, right parenthesis, end of line. -declare -a grep_post -grep_post=( - "\." - "\)" - "$" -) -declare -a sed_post -sed_post=( - "\." - ")" - "$" -) - -join () -{ - local or - or="$1" - shift - - local res - res="" - - local first - first=true - - for item in "$@"; do - if $first; then - first=false - res="$item" - else - res="$res$or$item" - fi - done - - echo "$res" -} - -grep_or="|" -sed_or="\|" - -grep_join () -{ - local res - res=$(join $grep_or "$@") - echo "($res)" -} - -sed_join () -{ - local res - res=$(join $sed_or "$@") - echo "\($res\)" -} - -usage () -{ - echo "usage: $(basename "$0") [--check] <file|dir>+" - echo " $(basename "$0") --print-dictionary" -} - -make_absolute () -{ - local arg - arg="$1" - - case "$arg" in - /*) - ;; - *) - arg=$(pwd -P)/"$arg" - ;; - esac - - echo "$arg" -} - -parse_args () -{ - local files - files=$(mktemp) - trap 'rm -f "$files"' EXIT - - if [ $# -eq 1 ] && [ "$1" = "--print-dictionary" ]; then - print_dictionary=true - return - fi - - while true; do - case " $1 " in - " --check ") - check=true - shift - ;; - *) - break - ;; - esac - done - - if [ $# -eq -0 ]; then - usage - exit 1 - fi - - local arg - for arg in "$@"; do - if [ -f "$arg" ]; then - arg=$(make_absolute "$arg") - readlink -e "$arg" \ - >> "$files" - elif [ -d "$arg" ]; then - arg=$(make_absolute "$arg") - local f - find "$arg" -type f -exec readlink -e {} \; \ - >> "$files" - else - echo "Not a file or directory: $arg" - exit 1 - fi - done - - mapfile -t unique_files \ - < <(sort -u "$files" \ - | grep -v ChangeLog) - - rm -f "$files" - trap "" EXIT -} - -get_dictionary () -{ - if [ -f "$dictionary" ]; then - return - fi - - local webpage - webpage=$(mktemp) - trap 'rm -f "$webpage"' EXIT - - # Download web page containing table. - wget $url -O "$webpage" - - # Extract table from web page. - awk '/<pre>/,/<\/pre>/' "$webpage" \ - | sed 's/<pre>//;s/<\/pre>//' \ - | grep -E -v "^$" \ - > "$dictionary" - - rm -f "$webpage" - trap "" EXIT -} - -output_local_dictionary () -{ - # Filter out comments and empty lines. - grep -E -v \ - "^#|^$" \ - "$local_dictionary" -} - -output_dictionaries () -{ - ( - output_local_dictionary - cat "$dictionary" - ) | grep -E -v "[A-Z]" -} - -parse_dictionary () -{ - # Parse dictionary. - mapfile -t words \ - < <(awk -F '->' '{print $1}' <(output_dictionaries)) - mapfile -t replacements \ - < <(awk -F '->' '{print $2}' <(output_dictionaries)) - - local words_done - declare -A words_done - local i word replacement - i=0 - for word in "${words[@]}"; do - replacement=${replacements[i]} - - # Skip words that are already handled. This ensures that the local - # dictionary overrides the wiki dictionary. - if [ "${words_done[$word]}" == 1 ]; then - words[i]="" - replacements[i]="" - i=$((i + 1)) - continue - fi - words_done[$word]=1 - - # Skip identity rules. - if [ "$word" = "$replacement" ]; then - words[i]="" - replacements[i]="" - fi - - i=$((i + 1)) - done -} - -print_dictionary () -{ - local i word replacement - i=0 - for word in "${words[@]}"; do - replacement=${replacements[i]} - i=$((i + 1)) - - if [ "$word" == "" ]; then - continue - fi - - echo "$word -> $replacement" - done -} - -find_files_matching_words () -{ - local cache_id - cache_id=$(cat "$local_dictionary" "$dictionary" "$this_script" \ - | md5sum \ - | awk '{print $1}') - - local patfile - patfile="$cache_dir/$cache_file2".$cache_id - - local pat - if [ -f "$patfile" ]; then - pat=$(cat "$patfile") - else - rm -f "$cache_dir/$cache_file2".* - - declare -a re_words - mapfile -t re_words \ - < <(for f in "${words[@]}"; do - if [ "$f" = "" ]; then - continue - fi - echo "$f" - done \ - | sed "s/^\(.\)/[\u\1\1]/") - - pat=$(grep_join "${re_words[@]}") - - local before after - before=$(grep_join \ - "${grep_pre[@]}" \ - "${grep_separators[@]}") - after=$(grep_join \ - "${grep_separators[@]}" \ - "${grep_post[@]}") - - pat="$before$pat$after" - - echo "$pat" \ - > "$patfile" - fi - - grep -E \ - -l \ - "$pat" \ - "$@" -} - -find_files_matching_word () -{ - local pat - pat="$1" - shift - - local before after - before=$(grep_join \ - "${grep_pre[@]}" \ - "${grep_separators[@]}") - after=$(grep_join \ - "${grep_separators[@]}" \ - "${grep_post[@]}") - - if bash_version_at_least 5 1; then - patc=${pat@u} - else - # shellcheck disable=SC2001 - patc=$(echo "$pat" | sed 's/^\(.\)/\u\1/') - fi - pat="($patc|$pat)" - - pat="$before$pat$after" - - grep -E \ - -l \ - "$pat" \ - "$@" -} - -replace_word_in_file () -{ - local word - word="$1" - - local replacement - replacement="$2" - - local file - file="$3" - - local before after - before=$(sed_join \ - "${sed_pre[@]}" \ - "${sed_separators[@]}") - after=$(sed_join \ - "${sed_separators[@]}" \ - "${sed_post[@]}") - - if bash_version_at_least 5 1; then - wordc=${word@u} - replacementc=${replacement@u} - else - # shellcheck disable=SC2001 - wordc=$(echo "$word" | sed 's/^\(.\)/\u\1/') - # shellcheck disable=SC2001 - replacementc=$(echo "$replacement" | sed 's/^\(.\)/\u\1/') - fi - - local repl1 - local repl2 - repl1="s%$before$word$after%\1$replacement\2%g" - repl2="s%$before$wordc$after%\1$replacementc\2%g" - - sed -i \ - "$repl1;$repl2" \ - "$file" -} - -replace_word_in_files () -{ - local word - word="$1" - - local replacement - replacement="$2" - - shift 2 - - local id - id="$word -> $replacement" - - # Reduce set of files for sed to operate on. - local files_matching_word - declare -a files_matching_word - mapfile -t files_matching_word \ - < <(find_files_matching_word "$word" "$@") - - if [ ${#files_matching_word[@]} -eq 0 ]; then - return - fi - - if echo "$replacement"| grep -q ","; then - echo "TODO: $id" - return - fi - - declare -A md5sums - - local changed f before after - changed=false - for f in "${files_matching_word[@]}"; do - if [ "${md5sums[$f]}" = "" ]; then - md5sums[$f]=$(md5sum "$f") - fi - - before="${md5sums[$f]}" - - replace_word_in_file \ - "$word" \ - "$replacement" \ - "$f" - - after=$(md5sum "$f") - - if [ "$after" != "$before" ]; then - md5sums[$f]="$after" - changed=true - fi - done - - if $changed; then - echo "$id" - fi - - find_files_matching_word "$word" "${files_matching_word[@]}" \ - | awk "{ printf \"TODO: $id: replacement failed: %s\n\", \$0}" -} - -main () -{ - declare -a unique_files - check=false - print_dictionary=false - parse_args "$@" - - get_dictionary - - declare -a words - declare -a replacements - parse_dictionary - - if $print_dictionary; then - print_dictionary - exit 0 - fi - - # Reduce set of files for sed to operate on. - local files_matching_words - declare -a files_matching_words - mapfile -t files_matching_words \ - < <(find_files_matching_words "${unique_files[@]}") - - if [ ${#files_matching_words[@]} -eq 0 ]; then - return - fi - - if $check; then - exit 1 - fi - - local i word replacement - i=0 - for word in "${words[@]}"; do - replacement=${replacements[i]} - i=$((i + 1)) - - if [ "$word" = "" ]; then - continue - fi - - replace_word_in_files \ - "$word" \ - "$replacement" \ - "${files_matching_words[@]}" - done -} - -main "$@" diff --git a/gdb/data-directory/Makefile.in b/gdb/data-directory/Makefile.in index 287dc7f..c08a68d 100644 --- a/gdb/data-directory/Makefile.in +++ b/gdb/data-directory/Makefile.in @@ -97,6 +97,7 @@ PYTHON_FILE_LIST = \ gdb/command/xmethods.py \ gdb/dap/breakpoint.py \ gdb/dap/bt.py \ + gdb/dap/completions.py \ gdb/dap/disassemble.py \ gdb/dap/evaluate.py \ gdb/dap/events.py \ diff --git a/gdb/dicos-tdep.c b/gdb/dicos-tdep.c index 3627426..96b841a 100644 --- a/gdb/dicos-tdep.c +++ b/gdb/dicos-tdep.c @@ -53,9 +53,7 @@ dicos_init_abi (struct gdbarch *gdbarch) int dicos_load_module_p (bfd *abfd, int header_size) { - long storage_needed; int ret = 0; - asymbol **symbol_table = NULL; const char *symname = "Dicos_loadModuleInfo"; asection *section; @@ -75,42 +73,19 @@ dicos_load_module_p (bfd *abfd, int header_size) /* Dicos LMs always have a "Dicos_loadModuleInfo" symbol defined. Look for it. */ - storage_needed = bfd_get_symtab_upper_bound (abfd); - if (storage_needed < 0) - { - warning (_("Can't read elf symbols from %s: %s"), - bfd_get_filename (abfd), - bfd_errmsg (bfd_get_error ())); - return 0; - } + gdb::array_view<asymbol *> symbol_table + = gdb_bfd_canonicalize_symtab (abfd, false); - if (storage_needed > 0) + for (asymbol *sym : symbol_table) { - long i, symcount; - - symbol_table = (asymbol **) xmalloc (storage_needed); - symcount = bfd_canonicalize_symtab (abfd, symbol_table); - - if (symcount < 0) - warning (_("Can't read elf symbols from %s: %s"), - bfd_get_filename (abfd), - bfd_errmsg (bfd_get_error ())); - else + if (sym->name != NULL + && symname[0] == sym->name[0] + && strcmp (symname + 1, sym->name + 1) == 0) { - for (i = 0; i < symcount; i++) - { - asymbol *sym = symbol_table[i]; - if (sym->name != NULL - && symname[0] == sym->name[0] - && strcmp (symname + 1, sym->name + 1) == 0) - { - ret = 1; - break; - } - } + ret = 1; + break; } } - xfree (symbol_table); return ret; } diff --git a/gdb/dwarf2/abbrev-table-cache.h b/gdb/dwarf2/abbrev-table-cache.h index 8469948..d99fb8d 100644 --- a/gdb/dwarf2/abbrev-table-cache.h +++ b/gdb/dwarf2/abbrev-table-cache.h @@ -30,6 +30,9 @@ public: abbrev_table_cache () = default; DISABLE_COPY_AND_ASSIGN (abbrev_table_cache); + abbrev_table_cache (abbrev_table_cache &&) = default; + abbrev_table_cache &operator= (abbrev_table_cache &&) = default; + /* Find an abbrev table coming from the abbrev section SECTION at offset OFFSET. Return the table, or nullptr if it has not yet been registered. */ diff --git a/gdb/dwarf2/cooked-index-entry.c b/gdb/dwarf2/cooked-index-entry.c new file mode 100644 index 0000000..3e322f1 --- /dev/null +++ b/gdb/dwarf2/cooked-index-entry.c @@ -0,0 +1,242 @@ +/* Entry in the cooked index + + Copyright (C) 2022-2024 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/>. */ + +#include "dwarf2/cooked-index-entry.h" +#include "dwarf2/tag.h" +#include "gdbsupport/gdb-safe-ctype.h" +#include "gdbsupport/selftest.h" + +/* See cooked-index-entry.h. */ + +std::string +to_string (cooked_index_flag flags) +{ + static constexpr cooked_index_flag::string_mapping mapping[] = { + MAP_ENUM_FLAG (IS_MAIN), + MAP_ENUM_FLAG (IS_STATIC), + MAP_ENUM_FLAG (IS_LINKAGE), + MAP_ENUM_FLAG (IS_TYPE_DECLARATION), + MAP_ENUM_FLAG (IS_PARENT_DEFERRED), + }; + + return flags.to_string (mapping); +} + +/* See cooked-index-entry.h. */ + +int +cooked_index_entry::compare (const char *stra, const char *strb, + comparison_mode mode) +{ +#if defined (__GNUC__) && !defined (__clang__) && __GNUC__ <= 7 + /* Work around error with gcc 7.5.0. */ + auto munge = [] (char c) -> unsigned char +#else + auto munge = [] (char c) constexpr -> unsigned char +#endif + { + /* Treat '<' as if it ended the string. This lets something + like "func<t>" match "func<t<int>>". See the "Breakpoints in + template functions" section in the manual. */ + if (c == '<') + return '\0'; + return TOLOWER ((unsigned char) c); + }; + + unsigned char a = munge (*stra); + unsigned char b = munge (*strb); + + while (a != '\0' && b != '\0' && a == b) + { + a = munge (*++stra); + b = munge (*++strb); + } + + if (a == b) + return 0; + + /* When completing, if STRB ends earlier than STRA, consider them as + equal. */ + if (mode == COMPLETE && b == '\0') + return 0; + + return a < b ? -1 : 1; +} + +#if GDB_SELF_TEST + +namespace { + +void +test_compare () +{ + /* Convenience aliases. */ + const auto mode_compare = cooked_index_entry::MATCH; + const auto mode_sort = cooked_index_entry::SORT; + const auto mode_complete = cooked_index_entry::COMPLETE; + + SELF_CHECK (cooked_index_entry::compare ("abcd", "abcd", + mode_compare) == 0); + SELF_CHECK (cooked_index_entry::compare ("abcd", "abcd", + mode_complete) == 0); + + SELF_CHECK (cooked_index_entry::compare ("abcd", "ABCDE", + mode_compare) < 0); + SELF_CHECK (cooked_index_entry::compare ("ABCDE", "abcd", + mode_compare) > 0); + SELF_CHECK (cooked_index_entry::compare ("abcd", "ABCDE", + mode_complete) < 0); + SELF_CHECK (cooked_index_entry::compare ("ABCDE", "abcd", + mode_complete) == 0); + + SELF_CHECK (cooked_index_entry::compare ("name", "name<>", + mode_compare) == 0); + SELF_CHECK (cooked_index_entry::compare ("name<>", "name", + mode_compare) == 0); + SELF_CHECK (cooked_index_entry::compare ("name", "name<>", + mode_complete) == 0); + SELF_CHECK (cooked_index_entry::compare ("name<>", "name", + mode_complete) == 0); + + SELF_CHECK (cooked_index_entry::compare ("name<arg>", "name<arg>", + mode_compare) == 0); + SELF_CHECK (cooked_index_entry::compare ("name<arg>", "name<ag>", + mode_compare) == 0); + SELF_CHECK (cooked_index_entry::compare ("name<arg>", "name<arg>", + mode_complete) == 0); + SELF_CHECK (cooked_index_entry::compare ("name<arg>", "name<ag>", + mode_complete) == 0); + + SELF_CHECK (cooked_index_entry::compare ("name<arg<more>>", + "name<arg<more>>", + mode_compare) == 0); + SELF_CHECK (cooked_index_entry::compare ("name<arg>", + "name<arg<more>>", + mode_compare) == 0); + + SELF_CHECK (cooked_index_entry::compare ("name", "name<arg<more>>", + mode_compare) == 0); + SELF_CHECK (cooked_index_entry::compare ("name<arg<more>>", "name", + mode_compare) == 0); + SELF_CHECK (cooked_index_entry::compare ("name<arg<more>>", "name<arg<", + mode_compare) == 0); + SELF_CHECK (cooked_index_entry::compare ("name<arg<more>>", "name<arg<", + mode_complete) == 0); + + SELF_CHECK (cooked_index_entry::compare ("", "abcd", mode_compare) < 0); + SELF_CHECK (cooked_index_entry::compare ("", "abcd", mode_complete) < 0); + SELF_CHECK (cooked_index_entry::compare ("abcd", "", mode_compare) > 0); + SELF_CHECK (cooked_index_entry::compare ("abcd", "", mode_complete) == 0); + + SELF_CHECK (cooked_index_entry::compare ("func", "func<type>", + mode_sort) == 0); + SELF_CHECK (cooked_index_entry::compare ("func<type>", "func1", + mode_sort) < 0); +} + +} /* anonymous namespace */ + +#endif /* GDB_SELF_TEST */ + +/* See cooked-index-entry.h. */ + +bool +cooked_index_entry::matches (domain_search_flags kind) const +{ + /* Just reject type declarations. */ + if ((flags & IS_TYPE_DECLARATION) != 0) + return false; + + return tag_matches_domain (tag, kind, lang); +} + +/* See cooked-index-entry.h. */ + +const char * +cooked_index_entry::full_name (struct obstack *storage, + cooked_index_full_name_flag name_flags, + const char *default_sep) const +{ + const char *local_name = ((name_flags & FOR_MAIN) != 0) ? name : canonical; + + if ((flags & IS_LINKAGE) != 0 || get_parent () == nullptr) + return local_name; + + const char *sep = default_sep; + switch (lang) + { + case language_cplus: + case language_rust: + case language_fortran: + sep = "::"; + break; + + case language_ada: + if ((name_flags & FOR_ADA_LINKAGE_NAME) != 0) + { + sep = "__"; + break; + } + [[fallthrough]]; + case language_go: + case language_d: + sep = "."; + break; + + default: + if (sep == nullptr) + return local_name; + break; + } + + /* The FOR_ADA_LINKAGE_NAME flag should only affect Ada entries, so + disable it here if we don't need it. */ + if (lang != language_ada) + name_flags &= ~FOR_ADA_LINKAGE_NAME; + + get_parent ()->write_scope (storage, sep, name_flags); + obstack_grow0 (storage, local_name, strlen (local_name)); + return (const char *) obstack_finish (storage); +} + +/* See cooked-index-entry.h. */ + +void +cooked_index_entry::write_scope (struct obstack *storage, + const char *sep, + cooked_index_full_name_flag flags) const +{ + if (get_parent () != nullptr) + get_parent ()->write_scope (storage, sep, flags); + /* When computing the Ada linkage name, the entry might not have + been canonicalized yet, so use the 'name'. */ + const char *local_name = ((flags & (FOR_MAIN | FOR_ADA_LINKAGE_NAME)) != 0 + ? name + : canonical); + obstack_grow (storage, local_name, strlen (local_name)); + obstack_grow (storage, sep, strlen (sep)); +} + +void _initialize_dwarf2_entry (); +void _initialize_dwarf2_entry () +{ +#if GDB_SELF_TEST + selftests::register_test ("cooked_index_entry::compare", test_compare); +#endif +} diff --git a/gdb/dwarf2/cooked-index-entry.h b/gdb/dwarf2/cooked-index-entry.h new file mode 100644 index 0000000..bb47e32 --- /dev/null +++ b/gdb/dwarf2/cooked-index-entry.h @@ -0,0 +1,258 @@ +/* Entry in the cooked index + + Copyright (C) 2022-2024 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_ENTRY_H +#define GDB_DWARF2_COOKED_INDEX_ENTRY_H + +#include "dwarf2/parent-map.h" +#include "dwarf2/types.h" +#include "symtab.h" +#include "gdbsupport/gdb_obstack.h" +#include "quick-symbol.h" + +/* Flags that describe an entry in the index. */ +enum cooked_index_flag_enum : unsigned char +{ + /* True if this entry is the program's "main". */ + IS_MAIN = 1, + /* True if this entry represents a "static" object. */ + IS_STATIC = 2, + /* True if this entry uses the linkage name. */ + IS_LINKAGE = 4, + /* True if this entry is just for the declaration of a type, not the + definition. */ + IS_TYPE_DECLARATION = 8, + /* True is parent_entry.deferred has a value rather than parent_entry + .resolved. */ + IS_PARENT_DEFERRED = 16, + /* True if this entry was synthesized by gdb (as opposed to coming + directly from the DWARF). */ + IS_SYNTHESIZED = 32, +}; +DEF_ENUM_FLAGS_TYPE (enum cooked_index_flag_enum, cooked_index_flag); + +/* Flags used when requesting the full name of an entry. */ +enum cooked_index_full_name_enum : unsigned char +{ + /* Set when requesting the name of "main". See the method for the + full description. */ + FOR_MAIN = 1, + /* Set when requesting the linkage name for an Ada entry. */ + FOR_ADA_LINKAGE_NAME = 2, +}; +DEF_ENUM_FLAGS_TYPE (enum cooked_index_full_name_enum, cooked_index_full_name_flag); + +/* Type representing either a resolved or deferred cooked_index_entry. */ + +union cooked_index_entry_ref +{ + cooked_index_entry_ref (parent_map::addr_type deferred_) + { + deferred = deferred_; + } + + cooked_index_entry_ref (const cooked_index_entry *resolved_) + { + resolved = resolved_; + } + + const cooked_index_entry *resolved; + parent_map::addr_type deferred; +}; + +/* Return a string representation of FLAGS. */ + +std::string to_string (cooked_index_flag flags); + +/* A cooked_index_entry represents a single item in the index. Note + that two entries can be created for the same DIE -- one using the + name, and another one using the linkage name, if any. + + This is an "open" class and the members are all directly + accessible. It is read-only after the index has been fully read + and processed. */ +struct cooked_index_entry : public allocate_on_obstack<cooked_index_entry> +{ + cooked_index_entry (sect_offset die_offset_, enum dwarf_tag tag_, + cooked_index_flag flags_, + enum language lang_, const char *name_, + cooked_index_entry_ref parent_entry_, + dwarf2_per_cu *per_cu_) + : name (name_), + tag (tag_), + flags (flags_), + lang (lang_), + die_offset (die_offset_), + per_cu (per_cu_), + m_parent_entry (parent_entry_) + { + } + + /* Return true if this entry matches SEARCH_FLAGS. */ + bool matches (block_search_flags search_flags) const + { + /* Just reject type declarations. */ + if ((flags & IS_TYPE_DECLARATION) != 0) + return false; + + if ((search_flags & SEARCH_STATIC_BLOCK) != 0 + && (flags & IS_STATIC) != 0) + return true; + if ((search_flags & SEARCH_GLOBAL_BLOCK) != 0 + && (flags & IS_STATIC) == 0) + return true; + return false; + } + + /* Return true if this entry matches KIND. */ + bool matches (domain_search_flags kind) const; + + /* Construct the fully-qualified name of this entry and return a + pointer to it. If allocation is needed, it will be done on + STORAGE. + + FLAGS affects the result. If the FOR_MAIN flag is set, we are + computing the name of the "main" entry -- one marked + DW_AT_main_subprogram. This matters for avoiding name + canonicalization and also a related race (if "main" computation + is done during finalization). + + If the FOR_ADA_LINKAGE_NAME flag is set, then Ada-language + symbols will have their "linkage-style" name computed. The + default is source-style. + + If the language doesn't prescribe a separator, one can be + specified using DEFAULT_SEP. */ + const char *full_name (struct obstack *storage, + cooked_index_full_name_flag name_flags = 0, + const char *default_sep = nullptr) const; + + /* Comparison modes for the 'compare' function. See the function + for a description. */ + enum comparison_mode + { + MATCH, + SORT, + COMPLETE, + }; + + /* Compare two strings, case-insensitively. Return -1 if STRA is + less than STRB, 0 if they are equal, and 1 if STRA is greater. + + When comparing, '<' is considered to be less than all other + printable characters. This ensures that "t<x>" sorts before + "t1", which is necessary when looking up "t". This '<' handling + is to ensure that certain C++ lookups work correctly. It is + inexact, and applied regardless of the search language, but this + is ok because callers of this code do more precise filtering + according to their needs. This is also why using a + case-insensitive comparison works even for languages that are + case sensitive. + + MODE controls how the comparison proceeds. + + MODE==SORT is used when sorting and the only special '<' handling + that it does is to ensure that '<' sorts before all other + printable characters. This ensures that the resulting ordering + will be binary-searchable. + + MODE==MATCH is used when searching for a symbol. In this case, + STRB must always be the search name, and STRA must be the name in + the index that is under consideration. In compare mode, early + termination of STRB may match STRA -- for example, "t<int>" and + "t" will be considered to be equal. (However, if A=="t" and + B=="t<int>", then this will not consider them as equal.) + + MODE==COMPLETE is used when searching for a symbol for + completion. In this case, STRB must always be the search name, + and STRA must be the name in the index that is under + consideration. In completion mode, early termination of STRB + always results in a match. */ + static int compare (const char *stra, const char *strb, + comparison_mode mode); + + /* Compare two entries by canonical name. */ + bool operator< (const cooked_index_entry &other) const + { + return compare (canonical, other.canonical, SORT) < 0; + } + + /* Set parent entry to PARENT. */ + void set_parent (const cooked_index_entry *parent) + { + gdb_assert ((flags & IS_PARENT_DEFERRED) == 0); + m_parent_entry.resolved = parent; + } + + /* Resolve deferred parent entry to PARENT. */ + void resolve_parent (const cooked_index_entry *parent) + { + gdb_assert ((flags & IS_PARENT_DEFERRED) != 0); + flags = flags & ~IS_PARENT_DEFERRED; + m_parent_entry.resolved = parent; + } + + /* Return parent entry. */ + const cooked_index_entry *get_parent () const + { + gdb_assert ((flags & IS_PARENT_DEFERRED) == 0); + return m_parent_entry.resolved; + } + + /* Return deferred parent entry. */ + parent_map::addr_type get_deferred_parent () const + { + gdb_assert ((flags & IS_PARENT_DEFERRED) != 0); + return m_parent_entry.deferred; + } + + /* 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 + attributes. */ + const char *name; + /* The canonical name. This may be equal to NAME. */ + const char *canonical = nullptr; + /* The DWARF tag. */ + 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 offset of this DIE. */ + sect_offset die_offset; + /* The CU from which this entry originates. */ + dwarf2_per_cu *per_cu; + +private: + + /* A helper method for full_name. Emits the full scope of this + object, followed by the separator, to STORAGE. If this entry has + a parent, its write_scope method is called first. See full_name + for a description of the FLAGS parameter. */ + void write_scope (struct obstack *storage, const char *sep, + cooked_index_full_name_flag flags) const; + + /* The parent entry. This is NULL for top-level entries. + Otherwise, it points to the parent entry, such as a namespace or + class. */ + cooked_index_entry_ref m_parent_entry; +}; + +#endif /* GDB_DWARF2_COOKED_INDEX_ENTRY_H */ diff --git a/gdb/dwarf2/cooked-index-shard.c b/gdb/dwarf2/cooked-index-shard.c new file mode 100644 index 0000000..683feb2 --- /dev/null +++ b/gdb/dwarf2/cooked-index-shard.c @@ -0,0 +1,331 @@ +/* Shards for the cooked index + + Copyright (C) 2022-2024 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/>. */ + +#include "dwarf2/cooked-index-shard.h" +#include "dwarf2/tag.h" +#include "dwarf2/index-common.h" +#include "cp-support.h" +#include "c-lang.h" +#include "ada-lang.h" + +/* Return true if a plain "main" could be the main program for this + language. Languages that are known to use some other mechanism are + excluded here. */ + +static bool +language_may_use_plain_main (enum language lang) +{ + /* No need to handle "unknown" here. */ + return (lang == language_c + || lang == language_objc + || lang == language_cplus + || lang == language_m2 + || lang == language_asm + || lang == language_opencl + || lang == language_minimal); +} + +/* See cooked-index-shard.h. */ + +cooked_index_entry * +cooked_index_shard::create (sect_offset die_offset, + enum dwarf_tag tag, + cooked_index_flag flags, + enum language lang, + const char *name, + cooked_index_entry_ref parent_entry, + dwarf2_per_cu *per_cu) +{ + if (tag == DW_TAG_module || tag == DW_TAG_namespace) + flags &= ~IS_STATIC; + else if (lang == language_cplus + && (tag == DW_TAG_class_type + || tag == DW_TAG_interface_type + || tag == DW_TAG_structure_type + || tag == DW_TAG_union_type + || tag == DW_TAG_enumeration_type + || tag == DW_TAG_enumerator)) + flags &= ~IS_STATIC; + else if (tag_is_type (tag)) + flags |= IS_STATIC; + + return new (&m_storage) cooked_index_entry (die_offset, tag, flags, + lang, name, parent_entry, + per_cu); +} + +/* See cooked-index-shard.h. */ + +cooked_index_entry * +cooked_index_shard::add (sect_offset die_offset, enum dwarf_tag tag, + cooked_index_flag flags, enum language lang, + const char *name, cooked_index_entry_ref parent_entry, + dwarf2_per_cu *per_cu) +{ + cooked_index_entry *result = create (die_offset, tag, flags, lang, name, + parent_entry, per_cu); + m_entries.push_back (result); + + /* An explicitly-tagged main program should always override the + implicit "main" discovery. */ + if ((flags & IS_MAIN) != 0) + m_main = result; + else if ((flags & IS_PARENT_DEFERRED) == 0 + && parent_entry.resolved == nullptr + && m_main == nullptr + && language_may_use_plain_main (lang) + && strcmp (name, "main") == 0) + m_main = result; + + return result; +} + +/* See cooked-index-shard.h. */ + +void +cooked_index_shard::handle_gnat_encoded_entry + (cooked_index_entry *entry, + htab_t gnat_entries, + std::vector<cooked_index_entry *> &new_entries) +{ + /* We decode Ada names in a particular way: operators and wide + characters are left as-is. This is done to make name matching a + bit simpler; and for wide characters, it means the choice of Ada + source charset does not affect the indexer directly. */ + std::string canonical = ada_decode (entry->name, false, false, false); + if (canonical.empty ()) + { + entry->canonical = entry->name; + return; + } + std::vector<std::string_view> names = split_name (canonical.c_str (), + split_style::DOT_STYLE); + std::string_view tail = names.back (); + names.pop_back (); + + const cooked_index_entry *parent = nullptr; + for (const auto &name : names) + { + uint32_t hashval = dwarf5_djb_hash (name); + void **slot = htab_find_slot_with_hash (gnat_entries, &name, + hashval, INSERT); + /* CUs are processed in order, so we only need to check the most + recent entry. */ + cooked_index_entry *last = (cooked_index_entry *) *slot; + if (last == nullptr || last->per_cu != entry->per_cu) + { + const char *new_name = m_names.insert (name); + last = create (entry->die_offset, DW_TAG_module, + IS_SYNTHESIZED, language_ada, new_name, parent, + entry->per_cu); + last->canonical = last->name; + new_entries.push_back (last); + *slot = last; + } + + parent = last; + } + + entry->set_parent (parent); + entry->canonical = m_names.insert (tail); +} + +/* Hash a cooked index entry by name pointer value. + + We can use pointer equality here because names come from .debug_str, which + will normally be unique-ified by the linker. Also, duplicates are relatively + harmless -- they just mean a bit of extra memory is used. */ + +struct cooked_index_entry_name_ptr_hash +{ + using is_avalanching = void; + + std::uint64_t operator () (const cooked_index_entry *entry) const noexcept + { + return ankerl::unordered_dense::hash<const char *> () (entry->name); + } +}; + +/* Compare cooked index entries by name pointer value. */ + +struct cooked_index_entry_name_ptr_eq +{ + bool operator () (const cooked_index_entry *a, + const cooked_index_entry *b) const noexcept + { + return a->name == b->name; + } +}; + +/* See cooked-index-shard.h. */ + +void +cooked_index_shard::finalize (const parent_map_map *parent_maps) +{ + gdb::unordered_set<const cooked_index_entry *, + cooked_index_entry_name_ptr_hash, + cooked_index_entry_name_ptr_eq> seen_names; + + auto hash_entry = [] (const void *e) + { + const cooked_index_entry *entry = (const cooked_index_entry *) e; + return dwarf5_djb_hash (entry->canonical); + }; + + auto eq_entry = [] (const void *a, const void *b) -> int + { + const cooked_index_entry *ae = (const cooked_index_entry *) a; + const std::string_view *sv = (const std::string_view *) b; + return (strlen (ae->canonical) == sv->length () + && strncasecmp (ae->canonical, sv->data (), sv->length ()) == 0); + }; + + htab_up gnat_entries (htab_create_alloc (10, hash_entry, eq_entry, + nullptr, xcalloc, xfree)); + std::vector<cooked_index_entry *> new_gnat_entries; + + for (cooked_index_entry *entry : m_entries) + { + if ((entry->flags & IS_PARENT_DEFERRED) != 0) + { + const cooked_index_entry *new_parent + = parent_maps->find (entry->get_deferred_parent ()); + entry->resolve_parent (new_parent); + } + + /* Note that this code must be kept in sync with + language_requires_canonicalization. */ + gdb_assert (entry->canonical == nullptr); + if ((entry->flags & IS_LINKAGE) != 0) + entry->canonical = entry->name; + else if (entry->lang == language_ada) + { + /* Newer versions of GNAT emit DW_TAG_module and use a + hierarchical structure. In this case, we don't need to + do any extra work. This can be detected by looking for a + GNAT-encoded name. */ + if (strstr (entry->name, "__") == nullptr) + { + entry->canonical = entry->name; + + /* If the entry does not have a parent, then there's + nothing extra to do here -- the entry itself is + sufficient. + + However, if it does have a parent, we have to + synthesize an entry with the full name. This is + unfortunate, but it's necessary due to how some of + the Ada name-lookup code currently works. For + example, without this, ada_get_tsd_type will + fail. + + Eventually it would be good to change the Ada lookup + code, and then remove these entries (and supporting + code in cooked_index_entry::full_name). */ + if (entry->get_parent () != nullptr) + { + const char *fullname + = entry->full_name (&m_storage, FOR_ADA_LINKAGE_NAME); + cooked_index_entry *linkage = create (entry->die_offset, + entry->tag, + (entry->flags + | IS_LINKAGE + | IS_SYNTHESIZED), + language_ada, + fullname, + nullptr, + entry->per_cu); + linkage->canonical = fullname; + new_gnat_entries.push_back (linkage); + } + } + else + handle_gnat_encoded_entry (entry, gnat_entries.get (), + new_gnat_entries); + } + else if (entry->lang == language_cplus || entry->lang == language_c) + { + auto [it, inserted] = seen_names.insert (entry); + + if (inserted) + { + /* No entry with that name was present, compute the canonical + name. */ + gdb::unique_xmalloc_ptr<char> canon_name + = (entry->lang == language_cplus + ? cp_canonicalize_string (entry->name) + : c_canonicalize_name (entry->name)); + if (canon_name == nullptr) + entry->canonical = entry->name; + else + entry->canonical = m_names.insert (std::move (canon_name)); + } + else + { + /* An entry with that name was present, re-use its canonical + name. */ + entry->canonical = (*it)->canonical; + } + } + else + entry->canonical = entry->name; + } + + /* Make sure any new Ada entries end up in the results. This isn't + done when creating these new entries to avoid invalidating the + m_entries iterator used in the foreach above. */ + m_entries.insert (m_entries.end (), new_gnat_entries.begin (), + new_gnat_entries.end ()); + + m_entries.shrink_to_fit (); + std::sort (m_entries.begin (), m_entries.end (), + [] (const cooked_index_entry *a, const cooked_index_entry *b) + { + return *a < *b; + }); +} + +/* See cooked-index-shard.h. */ + +cooked_index_shard::range +cooked_index_shard::find (const std::string &name, bool completing) const +{ + struct comparator + { + cooked_index_entry::comparison_mode mode; + + bool operator() (const cooked_index_entry *entry, + const char *name) const noexcept + { + return cooked_index_entry::compare (entry->canonical, name, mode) < 0; + } + + bool operator() (const char *name, + const cooked_index_entry *entry) const noexcept + { + return cooked_index_entry::compare (entry->canonical, name, mode) > 0; + } + }; + + return std::make_from_tuple<range> + (std::equal_range (m_entries.cbegin (), m_entries.cend (), name.c_str (), + comparator { (completing + ? cooked_index_entry::COMPLETE + : cooked_index_entry::MATCH) })); +} diff --git a/gdb/dwarf2/cooked-index-shard.h b/gdb/dwarf2/cooked-index-shard.h new file mode 100644 index 0000000..eb80926 --- /dev/null +++ b/gdb/dwarf2/cooked-index-shard.h @@ -0,0 +1,134 @@ +/* Shards for the cooked index + + Copyright (C) 2022-2024 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_SHARD_H +#define GDB_DWARF2_COOKED_INDEX_SHARD_H + +#include "dwarf2/cooked-index-entry.h" +#include "dwarf2/types.h" +#include "gdbsupport/gdb_obstack.h" +#include "addrmap.h" +#include "gdbsupport/iterator-range.h" +#include "gdbsupport/string-set.h" + +/* An index of interesting DIEs. This is "cooked", in contrast to a + mapped .debug_names or .gdb_index, which are "raw". An entry in + the index is of type cooked_index_entry. + + Operations on the index are described below. They are chosen to + make it relatively simple to implement the symtab "quick" + methods. */ +class cooked_index_shard +{ +public: + cooked_index_shard () = default; + DISABLE_COPY_AND_ASSIGN (cooked_index_shard); + + /* Create a new cooked_index_entry and register it with this object. + Entries are owned by this object. The new item is returned. */ + cooked_index_entry *add (sect_offset die_offset, enum dwarf_tag tag, + cooked_index_flag flags, enum language lang, + const char *name, + cooked_index_entry_ref parent_entry, + dwarf2_per_cu *per_cu); + + /* Install a new fixed addrmap from the given mutable addrmap. */ + void install_addrmap (addrmap_mutable *map) + { + gdb_assert (m_addrmap == nullptr); + m_addrmap = new (&m_storage) addrmap_fixed (&m_storage, map); + } + + friend class cooked_index; + + /* A simple range over part of m_entries. */ + typedef iterator_range<std::vector<cooked_index_entry *>::const_iterator> + range; + + /* Return a range of all the entries. */ + range all_entries () const + { + return { m_entries.cbegin (), m_entries.cend () }; + } + + /* 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) const; + +private: + + /* 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 + { + return m_main; + } + + /* 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) + { + if (m_addrmap == nullptr) + return nullptr; + + return (static_cast<dwarf2_per_cu *> (m_addrmap->find ((CORE_ADDR) addr))); + } + + /* Create a new cooked_index_entry and register it with this object. + Entries are owned by this object. The new item is returned. */ + cooked_index_entry *create (sect_offset die_offset, + enum dwarf_tag tag, + cooked_index_flag flags, + enum language lang, + const char *name, + cooked_index_entry_ref parent_entry, + dwarf2_per_cu *per_cu); + + /* When GNAT emits mangled ("encoded") names in the DWARF, and does + not emit the module structure, we still need this structuring to + do lookups. This function recreates that information for an + existing entry, modifying ENTRY as appropriate. Any new entries + are added to NEW_ENTRIES. */ + void handle_gnat_encoded_entry + (cooked_index_entry *entry, htab_t gnat_entries, + std::vector<cooked_index_entry *> &new_entries); + + /* Finalize the index. This should be called a single time, when + the index has been fully populated. It enters all the entries + into the internal table and fixes up all missing parent links. + This may be invoked in a worker thread. */ + void finalize (const parent_map_map *parent_maps); + + /* Storage for the entries. */ + auto_obstack m_storage; + /* List of all entries. */ + std::vector<cooked_index_entry *> m_entries; + /* If we found an entry with 'is_main' set, store it here. */ + cooked_index_entry *m_main = nullptr; + /* The addrmap. This maps address ranges to dwarf2_per_cu objects. */ + addrmap_fixed *m_addrmap = nullptr; + /* Storage for canonical names. */ + gdb::string_set m_names; +}; + +using cooked_index_shard_up = std::unique_ptr<cooked_index_shard>; + +#endif /* GDB_DWARF2_COOKED_INDEX_SHARD_H */ diff --git a/gdb/dwarf2/cooked-index-storage.c b/gdb/dwarf2/cooked-index-storage.c deleted file mode 100644 index 9c05cf5..0000000 --- a/gdb/dwarf2/cooked-index-storage.c +++ /dev/null @@ -1,84 +0,0 @@ -/* DWARF index storage - - 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/>. */ - -#include "dwarf2/cooked-index-storage.h" - -/* See cooked-index-storage.h. */ - -cooked_index_storage::cooked_index_storage () - : m_shard (new cooked_index_shard) -{ -} - -/* See cooked-index-storage.h. */ - -cutu_reader * -cooked_index_storage::get_reader (dwarf2_per_cu *per_cu) -{ - auto it = m_reader_hash.find (*per_cu); - return it != m_reader_hash.end () ? it->get () : nullptr; -} - -/* See cooked-index-storage.h. */ - -cutu_reader * -cooked_index_storage::preserve (cutu_reader_up reader) -{ - m_abbrev_table_cache.add (reader->release_abbrev_table ()); - - auto [it, inserted] = m_reader_hash.insert (std::move (reader)); - gdb_assert (inserted); - - return it->get(); -} - -/* See cooked-index-storage.h. */ - -std::uint64_t -cooked_index_storage::cutu_reader_hash::operator() - (const cutu_reader_up &reader) const noexcept -{ - return (*this) (*reader->cu ()->per_cu); -} - -/* See cooked-index-storage.h. */ - -std::uint64_t -cooked_index_storage::cutu_reader_hash::operator() (const dwarf2_per_cu &per_cu) - const noexcept -{ - return per_cu.index; -} - -/* See cooked-index-storage.h. */ - -bool -cooked_index_storage::cutu_reader_eq::operator() (const cutu_reader_up &a, - const cutu_reader_up &b) const noexcept -{ - return (*this) (*a->cu ()->per_cu, b); -} - -/* See cooked-index-storage.h. */ - -bool cooked_index_storage::cutu_reader_eq::operator() - (const dwarf2_per_cu &per_cu, const cutu_reader_up &reader) const noexcept -{ - return per_cu.index == reader->cu ()->per_cu->index; -} diff --git a/gdb/dwarf2/cooked-index-storage.h b/gdb/dwarf2/cooked-index-storage.h deleted file mode 100644 index 449fbe1..0000000 --- a/gdb/dwarf2/cooked-index-storage.h +++ /dev/null @@ -1,131 +0,0 @@ -/* DWARF index storage - - 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_STORAGE_H -#define GDB_DWARF2_COOKED_INDEX_STORAGE_H - -#include "dwarf2/abbrev-table-cache.h" -#include "dwarf2/cooked-index.h" -#include "dwarf2/types.h" - -struct cutu_reader; -struct dwarf2_per_cu; - -using cutu_reader_up = std::unique_ptr<cutu_reader>; - -/* An instance of this is created when scanning DWARF to create a - cooked index. */ - -class cooked_index_storage -{ -public: - - cooked_index_storage (); - DISABLE_COPY_AND_ASSIGN (cooked_index_storage); - - /* Return the current abbrev table_cache. */ - const abbrev_table_cache &get_abbrev_table_cache () const - { return m_abbrev_table_cache; } - - /* Return the DIE reader corresponding to PER_CU. If no such reader - has been registered, return NULL. */ - cutu_reader *get_reader (dwarf2_per_cu *per_cu); - - /* Preserve READER by storing it in the local hash table. */ - cutu_reader *preserve (cutu_reader_up reader); - - /* 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); - } - - /* Install the current addrmap into the shard being constructed, - then transfer ownership of the index to the caller. */ - cooked_index_shard_up release () - { - m_shard->install_addrmap (&m_addrmap); - return std::move (m_shard); - } - - /* Return the mutable addrmap that is currently being created. */ - addrmap_mutable *get_addrmap () - { - return &m_addrmap; - } - - /* Return the parent_map that is currently being created. */ - parent_map *get_parent_map () - { - return &m_parent_map; - } - - /* Return the parent_map that is currently being created. Ownership - is passed to the caller. */ - parent_map release_parent_map () - { - return std::move (m_parent_map); - } - -private: - /* The abbrev table cache used by this indexer. */ - abbrev_table_cache m_abbrev_table_cache; - - /* Hash function for a cutu_reader. */ - struct cutu_reader_hash - { - using is_transparent = void; - - std::uint64_t operator() (const cutu_reader_up &reader) const noexcept; - std::uint64_t operator() (const dwarf2_per_cu &per_cu) const noexcept; - }; - - /* Equality function for cutu_reader. */ - struct cutu_reader_eq - { - using is_transparent = void; - - bool operator() (const cutu_reader_up &a, - const cutu_reader_up &b) const noexcept; - - bool operator() (const dwarf2_per_cu &per_cu, - const cutu_reader_up &reader) const noexcept; - }; - - /* A hash table of cutu_reader objects. */ - gdb::unordered_set<cutu_reader_up, cutu_reader_hash, cutu_reader_eq> - m_reader_hash; - - /* The index shard that is being constructed. */ - cooked_index_shard_up m_shard; - - /* Parent map for each CU that is read. */ - parent_map m_parent_map; - - /* A writeable addrmap being constructed by this scanner. */ - addrmap_mutable m_addrmap; -}; - -#endif /* GDB_DWARF2_COOKED_INDEX_STORAGE_H */ diff --git a/gdb/dwarf2/cooked-index-worker.c b/gdb/dwarf2/cooked-index-worker.c new file mode 100644 index 0000000..95ec943 --- /dev/null +++ b/gdb/dwarf2/cooked-index-worker.c @@ -0,0 +1,265 @@ +/* DWARF index storage + + 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/>. */ + +#include "dwarf2/cooked-index-worker.h" +#include "dwarf2/cooked-index.h" +#include "gdbsupport/thread-pool.h" +#include "run-on-main-thread.h" +#include "event-top.h" +#include "exceptions.h" + +/* See cooked-index-worker.h. */ + +cooked_index_worker_result::cooked_index_worker_result () + : m_shard (new cooked_index_shard) +{ +} + +/* See cooked-index-worker.h. */ + +cutu_reader * +cooked_index_worker_result::get_reader (dwarf2_per_cu *per_cu) +{ + auto it = m_reader_hash.find (*per_cu); + return it != m_reader_hash.end () ? it->get () : nullptr; +} + +/* See cooked-index-worker.h. */ + +cutu_reader * +cooked_index_worker_result::preserve (cutu_reader_up reader) +{ + m_abbrev_table_cache.add (reader->release_abbrev_table ()); + + auto [it, inserted] = m_reader_hash.insert (std::move (reader)); + gdb_assert (inserted); + + return it->get(); +} + +/* See cooked-index-worker.h. */ + +std::uint64_t +cooked_index_worker_result::cutu_reader_hash::operator() + (const cutu_reader_up &reader) const noexcept +{ + return (*this) (*reader->cu ()->per_cu); +} + +/* See cooked-index-worker.h. */ + +std::uint64_t +cooked_index_worker_result::cutu_reader_hash::operator() (const dwarf2_per_cu &per_cu) + const noexcept +{ + return per_cu.index; +} + +/* See cooked-index-worker.h. */ + +bool +cooked_index_worker_result::cutu_reader_eq::operator() (const cutu_reader_up &a, + const cutu_reader_up &b) const noexcept +{ + return (*this) (*a->cu ()->per_cu, b); +} + +/* See cooked-index-worker.h. */ + +bool cooked_index_worker_result::cutu_reader_eq::operator() + (const dwarf2_per_cu &per_cu, const cutu_reader_up &reader) const noexcept +{ + return per_cu.index == reader->cu ()->per_cu->index; +} + +/* See cooked-index-worker.h. */ + +void +cooked_index_worker_result::emit_complaints_and_exceptions + (gdb::unordered_set<gdb_exception> &seen_exceptions) +{ + gdb_assert (is_main_thread ()); + + re_emit_complaints (m_complaints); + + /* Only show a given exception a single time. */ + for (auto &one_exc : m_exceptions) + if (seen_exceptions.insert (one_exc).second) + exception_print (gdb_stderr, one_exc); +} + +/* See cooked-index-worker.h. */ + +void +cooked_index_worker::start () +{ + gdb::thread_pool::g_thread_pool->post_task ([this] () + { + try + { + do_reading (); + } + catch (const gdb_exception &exc) + { + m_failed = exc; + set (cooked_state::CACHE_DONE); + } + + bfd_thread_cleanup (); + }); +} + +/* See cooked-index-worker.h. */ + +bool +cooked_index_worker::wait (cooked_state desired_state, bool allow_quit) +{ + bool done; +#if CXX_STD_THREAD + { + std::unique_lock<std::mutex> lock (m_mutex); + + /* This may be called from a non-main thread -- this functionality + is needed for the index cache -- but in this case we require + that the desired state already have been attained. */ + gdb_assert (is_main_thread () || desired_state <= m_state); + + while (desired_state > m_state) + { + if (allow_quit) + { + std::chrono::milliseconds duration { 15 }; + if (m_cond.wait_for (lock, duration) == std::cv_status::timeout) + QUIT; + } + else + m_cond.wait (lock); + } + done = m_state == cooked_state::CACHE_DONE; + } +#else + /* Without threads, all the work is done immediately on the main + thread, and there is never anything to wait for. */ + done = desired_state == cooked_state::CACHE_DONE; +#endif /* CXX_STD_THREAD */ + + /* Only the main thread is allowed to report complaints and the + like. */ + if (!is_main_thread ()) + return false; + + if (m_reported) + return done; + m_reported = true; + + /* Emit warnings first, maybe they were emitted before an exception + (if any) was thrown. */ + m_warnings.emit (); + + if (m_failed.has_value ()) + { + /* do_reading failed -- report it. */ + exception_print (gdb_stderr, *m_failed); + m_failed.reset (); + return done; + } + + /* Only show a given exception a single time. */ + gdb::unordered_set<gdb_exception> seen_exceptions; + for (auto &one_result : m_results) + one_result.emit_complaints_and_exceptions (seen_exceptions); + + print_stats (); + + struct objfile *objfile = m_per_objfile->objfile; + dwarf2_per_bfd *per_bfd = m_per_objfile->per_bfd; + cooked_index *table + = (gdb::checked_static_cast<cooked_index *> + (per_bfd->index_table.get ())); + + auto_obstack temp_storage; + enum language lang = language_unknown; + const char *main_name = table->get_main_name (&temp_storage, &lang); + if (main_name != nullptr) + set_objfile_main_name (objfile, main_name, lang); + + /* dwarf_read_debug_printf ("Done building psymtabs of %s", */ + /* objfile_name (objfile)); */ + + return done; +} + +/* See cooked-index-worker.h. */ + +void +cooked_index_worker::set (cooked_state desired_state) +{ + gdb_assert (desired_state != cooked_state::INITIAL); + +#if CXX_STD_THREAD + std::lock_guard<std::mutex> guard (m_mutex); + gdb_assert (desired_state > m_state); + m_state = desired_state; + m_cond.notify_one (); +#else + /* Without threads, all the work is done immediately on the main + thread, and there is never anything to do. */ +#endif /* CXX_STD_THREAD */ +} + +/* See cooked-index-worker.h. */ + +void +cooked_index_worker::write_to_cache (const cooked_index *idx, + deferred_warnings *warn) const +{ + if (idx != nullptr) + { + /* Writing to the index cache may cause a warning to be emitted. + See PR symtab/30837. This arranges to capture all such + warnings. This is safe because we know the deferred_warnings + object isn't in use by any other thread at this point. */ + scoped_restore_warning_hook defer (warn); + m_cache_store.store (); + } +} + +/* See cooked-index-worker.h. */ + +void +cooked_index_worker::done_reading () +{ + /* Only handle the scanning results here. Complaints and exceptions + can only be dealt with on the main thread. */ + std::vector<cooked_index_shard_up> shards; + + for (auto &one_result : m_results) + { + shards.push_back (one_result.release_shard ()); + m_all_parents_map.add_map (*one_result.get_parent_map ()); + } + + shards.shrink_to_fit (); + + dwarf2_per_bfd *per_bfd = m_per_objfile->per_bfd; + cooked_index *table + = (gdb::checked_static_cast<cooked_index *> + (per_bfd->index_table.get ())); + table->set_contents (std::move (shards), &m_warnings, &m_all_parents_map); +} diff --git a/gdb/dwarf2/cooked-index-worker.h b/gdb/dwarf2/cooked-index-worker.h new file mode 100644 index 0000000..fbbb3b5 --- /dev/null +++ b/gdb/dwarf2/cooked-index-worker.h @@ -0,0 +1,290 @@ +/* DWARF index storage + + 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_WORKER_H +#define GDB_DWARF2_COOKED_INDEX_WORKER_H + +#include "dwarf2/abbrev-table-cache.h" +#include "dwarf2/cooked-index-entry.h" +#include "dwarf2/cooked-index-shard.h" +#include "dwarf2/types.h" +#include "dwarf2/read.h" + +#if CXX_STD_THREAD +#include <mutex> +#include <condition_variable> +#endif /* CXX_STD_THREAD */ + +using cutu_reader_up = std::unique_ptr<cutu_reader>; + +/* An instance of this is created when scanning DWARF to create a + cooked index. This class is the result of a single task to store + results while working -- that is, it is an implementation detail of + the threads managed by cooked_index_worker. Once scanning is done, + selected parts of the state here are stored into the shard, and + then these temporary objects are destroyed. */ + +class cooked_index_worker_result +{ +public: + + cooked_index_worker_result (); + DISABLE_COPY_AND_ASSIGN (cooked_index_worker_result); + + cooked_index_worker_result (cooked_index_worker_result &&) = default; + cooked_index_worker_result &operator= (cooked_index_worker_result &&) + = default; + + /* Return the current abbrev table_cache. */ + const abbrev_table_cache &get_abbrev_table_cache () const + { return m_abbrev_table_cache; } + + /* Return the DIE reader corresponding to PER_CU. If no such reader + has been registered, return NULL. */ + cutu_reader *get_reader (dwarf2_per_cu *per_cu); + + /* Preserve READER by storing it in the local hash table. */ + cutu_reader *preserve (cutu_reader_up reader); + + /* 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, + cooked_index_entry_ref parent_entry, + dwarf2_per_cu *per_cu) + { + return m_shard->add (die_offset, tag, flags, lang, + name, parent_entry, per_cu); + } + + /* Install the current addrmap into the shard being constructed, + then transfer ownership of the index to the caller. */ + cooked_index_shard_up release_shard () + { + m_shard->install_addrmap (&m_addrmap); + /* This isn't needed any more. */ + m_addrmap.clear (); + return std::move (m_shard); + } + + /* Return the mutable addrmap that is currently being created. */ + addrmap_mutable *get_addrmap () + { + return &m_addrmap; + } + + /* Return the parent_map that is currently being created. */ + parent_map *get_parent_map () + { + return &m_parent_map; + } + + /* Add an exception to the list of exceptions caught while reading. + These are passed forward and printed by the main thread. */ + void note_error (gdb_exception &&except) + { + m_exceptions.push_back (std::move (except)); + } + + /* Called when the thread using this object is done with its work. + This stores any complaints for later emission, and it clears some + data that won't be needed again. */ + void done_reading (complaint_collection &&complaints) + { + /* Hang on to the complaints. */ + m_complaints = std::move (complaints); + /* Discard things that are no longer needed. */ + m_reader_hash.clear (); + } + + /* Called to emit any stored complaints or exceptions. This can + only be called on the main thread. */ + void emit_complaints_and_exceptions + (gdb::unordered_set<gdb_exception> &seen_exceptions); + +private: + /* The abbrev table cache used by this indexer. */ + abbrev_table_cache m_abbrev_table_cache; + + /* Hash function for a cutu_reader. */ + struct cutu_reader_hash + { + using is_transparent = void; + + std::uint64_t operator() (const cutu_reader_up &reader) const noexcept; + std::uint64_t operator() (const dwarf2_per_cu &per_cu) const noexcept; + }; + + /* Equality function for cutu_reader. */ + struct cutu_reader_eq + { + using is_transparent = void; + + bool operator() (const cutu_reader_up &a, + const cutu_reader_up &b) const noexcept; + + bool operator() (const dwarf2_per_cu &per_cu, + const cutu_reader_up &reader) const noexcept; + }; + + /* A hash table of cutu_reader objects. */ + gdb::unordered_set<cutu_reader_up, cutu_reader_hash, cutu_reader_eq> + m_reader_hash; + + /* The index shard that is being constructed. */ + cooked_index_shard_up m_shard; + + /* Parent map for each CU that is read. */ + parent_map m_parent_map; + + /* A writeable addrmap being constructed by this scanner. */ + addrmap_mutable m_addrmap; + + /* The issued complaints. Only set after done_reading is + called. */ + complaint_collection m_complaints; + + /* Exceptions that we're storing to emit later. */ + std::vector<gdb_exception> m_exceptions; +}; + +/* The possible states of the index. See the explanatory comment + before cooked_index for more details. */ +enum class cooked_state +{ + /* The default state. This is not a valid argument to 'wait'. */ + INITIAL, + /* The initial scan has completed. The name of "main" is now + available (if known). The addrmaps are usable now. + Finalization has started but is not complete. */ + MAIN_AVAILABLE, + /* Finalization has completed. This means the index is fully + available for queries. */ + FINALIZED, + /* Writing to the index cache has finished. */ + CACHE_DONE, +}; + +/* An object of this type controls the scanning of the DWARF. It + schedules the worker tasks and tracks the current state. Once + scanning is done, this object is discarded. + + This is an abstract base class that defines the basic behavior of + scanners. Separate concrete implementations exist for scanning + .debug_names and .debug_info. */ + +class cooked_index_worker +{ +public: + + explicit cooked_index_worker (dwarf2_per_objfile *per_objfile) + : m_per_objfile (per_objfile), + m_cache_store (global_index_cache, per_objfile->per_bfd) + { } + virtual ~cooked_index_worker () + { } + DISABLE_COPY_AND_ASSIGN (cooked_index_worker); + + /* Start reading. */ + void start (); + + /* Wait for a particular state to be achieved. If ALLOW_QUIT is + true, then the loop will check the QUIT flag. Normally this + method may only be called from the main thread; however, it can + be called from a worker thread provided that the desired state + has already been attained. (This oddity is used by the index + cache writer.) */ + bool wait (cooked_state desired_state, bool allow_quit); + +protected: + + /* Let cooked_index call the 'set' and 'write_to_cache' methods. */ + friend class cooked_index; + + /* Set the current state. */ + void set (cooked_state desired_state); + + /* Write to the index cache. */ + void write_to_cache (const cooked_index *idx, + deferred_warnings *warn) const; + + /* Helper function that does the work of reading. This must be able + to be run in a worker thread without problems. */ + virtual void do_reading () = 0; + + /* Helper function that should be called when done reading. This + assumes that m_results is filled in, and will initialize + m_all_parents_map and end by calling + cooked_index::set_contents. */ + virtual void done_reading (); + + /* A callback that can print stats, if needed. This is called when + transitioning to the 'MAIN_AVAILABLE' state. */ + virtual void print_stats () + { } + + /* The per-objfile object. */ + dwarf2_per_objfile *m_per_objfile; + /* Result of each worker task. */ + std::vector<cooked_index_worker_result> m_results; + /* Any warnings emitted. For the time being at least, this only + needed in do_reading, not in every worker. Note that + deferred_warnings uses gdb_stderr in its constructor, and this + should only be done from the main thread. This is enforced in + the cooked_index_worker constructor. */ + deferred_warnings m_warnings; + + /* A map of all parent maps. Used during finalization to fix up + parent relationships. */ + parent_map_map m_all_parents_map; + +#if CXX_STD_THREAD + /* Current state of this object. */ + cooked_state m_state = cooked_state::INITIAL; + /* Mutex and condition variable used to synchronize. */ + std::mutex m_mutex; + std::condition_variable m_cond; +#endif /* CXX_STD_THREAD */ + /* This flag indicates whether any complaints or exceptions that + arose during scanning have been reported by 'wait'. This may + only be modified on the main thread. */ + bool m_reported = false; + /* If set, an exception occurred during reading; in this case the + scanning is stopped and this exception will later be reported by + the 'wait' method. */ + std::optional<gdb_exception> m_failed; + /* An object used to write to the index cache. */ + index_cache_store_context m_cache_store; +}; + +using cooked_index_worker_up = std::unique_ptr<cooked_index_worker>; + +#endif /* GDB_DWARF2_COOKED_INDEX_WORKER_H */ diff --git a/gdb/dwarf2/cooked-index.c b/gdb/dwarf2/cooked-index.c index 724615f..a632474 100644 --- a/gdb/dwarf2/cooked-index.c +++ b/gdb/dwarf2/cooked-index.c @@ -18,24 +18,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "dwarf2/cooked-index.h" -#include "dwarf2/index-common.h" #include "dwarf2/read.h" #include "dwarf2/stringify.h" #include "dwarf2/index-cache.h" -#include "cp-support.h" -#include "c-lang.h" -#include "ada-lang.h" -#include "dwarf2/tag.h" #include "event-top.h" -#include "exceptions.h" #include "split-name.h" #include "observable.h" #include "run-on-main-thread.h" #include <algorithm> -#include "gdbsupport/gdb-safe-ctype.h" -#include "gdbsupport/selftest.h" #include "gdbsupport/task-group.h" -#include "gdbsupport/thread-pool.h" #include <chrono> #include "cli/cli-cmds.h" @@ -44,25 +35,12 @@ here, and then these are all waited for before exit proceeds. */ static gdb::unordered_set<cooked_index *> active_vectors; -/* See cooked-index.h. */ - -std::string -to_string (cooked_index_flag flags) -{ - static constexpr cooked_index_flag::string_mapping mapping[] = { - MAP_ENUM_FLAG (IS_MAIN), - MAP_ENUM_FLAG (IS_STATIC), - MAP_ENUM_FLAG (IS_LINKAGE), - MAP_ENUM_FLAG (IS_TYPE_DECLARATION), - MAP_ENUM_FLAG (IS_PARENT_DEFERRED), - }; - - return flags.to_string (mapping); -} - -/* See cooked-index.h. */ +/* Return true if LANG requires canonicalization. This is used + primarily to work around an issue computing the name of "main". + This function must be kept in sync with + cooked_index_shard::finalize. */ -bool +static bool language_requires_canonicalization (enum language lang) { return (lang == language_ada @@ -70,648 +48,6 @@ language_requires_canonicalization (enum language lang) || lang == language_cplus); } -/* Return true if a plain "main" could be the main program for this - language. Languages that are known to use some other mechanism are - excluded here. */ - -static bool -language_may_use_plain_main (enum language lang) -{ - /* No need to handle "unknown" here. */ - return (lang == language_c - || lang == language_objc - || lang == language_cplus - || lang == language_m2 - || lang == language_asm - || lang == language_opencl - || lang == language_minimal); -} - -/* See cooked-index.h. */ - -int -cooked_index_entry::compare (const char *stra, const char *strb, - comparison_mode mode) -{ -#if defined (__GNUC__) && !defined (__clang__) && __GNUC__ <= 7 - /* Work around error with gcc 7.5.0. */ - auto munge = [] (char c) -> unsigned char -#else - auto munge = [] (char c) constexpr -> unsigned char -#endif - { - /* Treat '<' as if it ended the string. This lets something - like "func<t>" match "func<t<int>>". See the "Breakpoints in - template functions" section in the manual. */ - if (c == '<') - return '\0'; - return TOLOWER ((unsigned char) c); - }; - - unsigned char a = munge (*stra); - unsigned char b = munge (*strb); - - while (a != '\0' && b != '\0' && a == b) - { - a = munge (*++stra); - b = munge (*++strb); - } - - if (a == b) - return 0; - - /* When completing, if STRB ends earlier than STRA, consider them as - equal. */ - if (mode == COMPLETE || (mode == MATCH && a == munge ('<'))) - { - if (b == '\0') - return 0; - } - - return a < b ? -1 : 1; -} - -#if GDB_SELF_TEST - -namespace { - -void -test_compare () -{ - /* Convenience aliases. */ - const auto mode_compare = cooked_index_entry::MATCH; - const auto mode_sort = cooked_index_entry::SORT; - const auto mode_complete = cooked_index_entry::COMPLETE; - - SELF_CHECK (cooked_index_entry::compare ("abcd", "abcd", - mode_compare) == 0); - SELF_CHECK (cooked_index_entry::compare ("abcd", "abcd", - mode_complete) == 0); - - SELF_CHECK (cooked_index_entry::compare ("abcd", "ABCDE", - mode_compare) < 0); - SELF_CHECK (cooked_index_entry::compare ("ABCDE", "abcd", - mode_compare) > 0); - SELF_CHECK (cooked_index_entry::compare ("abcd", "ABCDE", - mode_complete) < 0); - SELF_CHECK (cooked_index_entry::compare ("ABCDE", "abcd", - mode_complete) == 0); - - SELF_CHECK (cooked_index_entry::compare ("name", "name<>", - mode_compare) == 0); - SELF_CHECK (cooked_index_entry::compare ("name<>", "name", - mode_compare) == 0); - SELF_CHECK (cooked_index_entry::compare ("name", "name<>", - mode_complete) == 0); - SELF_CHECK (cooked_index_entry::compare ("name<>", "name", - mode_complete) == 0); - - SELF_CHECK (cooked_index_entry::compare ("name<arg>", "name<arg>", - mode_compare) == 0); - SELF_CHECK (cooked_index_entry::compare ("name<arg>", "name<ag>", - mode_compare) == 0); - SELF_CHECK (cooked_index_entry::compare ("name<arg>", "name<arg>", - mode_complete) == 0); - SELF_CHECK (cooked_index_entry::compare ("name<arg>", "name<ag>", - mode_complete) == 0); - - SELF_CHECK (cooked_index_entry::compare ("name<arg<more>>", - "name<arg<more>>", - mode_compare) == 0); - SELF_CHECK (cooked_index_entry::compare ("name<arg>", - "name<arg<more>>", - mode_compare) == 0); - - SELF_CHECK (cooked_index_entry::compare ("name", "name<arg<more>>", - mode_compare) == 0); - SELF_CHECK (cooked_index_entry::compare ("name<arg<more>>", "name", - mode_compare) == 0); - SELF_CHECK (cooked_index_entry::compare ("name<arg<more>>", "name<arg<", - mode_compare) == 0); - SELF_CHECK (cooked_index_entry::compare ("name<arg<more>>", "name<arg<", - mode_complete) == 0); - - SELF_CHECK (cooked_index_entry::compare ("", "abcd", mode_compare) < 0); - SELF_CHECK (cooked_index_entry::compare ("", "abcd", mode_complete) < 0); - SELF_CHECK (cooked_index_entry::compare ("abcd", "", mode_compare) > 0); - SELF_CHECK (cooked_index_entry::compare ("abcd", "", mode_complete) == 0); - - SELF_CHECK (cooked_index_entry::compare ("func", "func<type>", - mode_sort) == 0); - SELF_CHECK (cooked_index_entry::compare ("func<type>", "func1", - mode_sort) < 0); -} - -} /* anonymous namespace */ - -#endif /* GDB_SELF_TEST */ - -/* See cooked-index.h. */ - -bool -cooked_index_entry::matches (domain_search_flags kind) const -{ - /* Just reject type declarations. */ - if ((flags & IS_TYPE_DECLARATION) != 0) - return false; - - return tag_matches_domain (tag, kind, lang); -} - -/* See cooked-index.h. */ - -const char * -cooked_index_entry::full_name (struct obstack *storage, - cooked_index_full_name_flag name_flags, - const char *default_sep) const -{ - const char *local_name = ((name_flags & FOR_MAIN) != 0) ? name : canonical; - - if ((flags & IS_LINKAGE) != 0 || get_parent () == nullptr) - return local_name; - - const char *sep = default_sep; - switch (lang) - { - case language_cplus: - case language_rust: - case language_fortran: - sep = "::"; - break; - - case language_ada: - if ((name_flags & FOR_ADA_LINKAGE_NAME) != 0) - { - sep = "__"; - break; - } - [[fallthrough]]; - case language_go: - case language_d: - sep = "."; - break; - - default: - if (sep == nullptr) - return local_name; - break; - } - - /* The FOR_ADA_LINKAGE_NAME flag should only affect Ada entries, so - disable it here if we don't need it. */ - if (lang != language_ada) - name_flags &= ~FOR_ADA_LINKAGE_NAME; - - get_parent ()->write_scope (storage, sep, name_flags); - obstack_grow0 (storage, local_name, strlen (local_name)); - return (const char *) obstack_finish (storage); -} - -/* See cooked-index.h. */ - -void -cooked_index_entry::write_scope (struct obstack *storage, - const char *sep, - cooked_index_full_name_flag flags) const -{ - if (get_parent () != nullptr) - get_parent ()->write_scope (storage, sep, flags); - /* When computing the Ada linkage name, the entry might not have - been canonicalized yet, so use the 'name'. */ - const char *local_name = ((flags & (FOR_MAIN | FOR_ADA_LINKAGE_NAME)) != 0 - ? name - : canonical); - obstack_grow (storage, local_name, strlen (local_name)); - obstack_grow (storage, sep, strlen (sep)); -} - -/* See cooked-index.h. */ - -cooked_index_entry * -cooked_index_shard::create (sect_offset die_offset, - enum dwarf_tag tag, - cooked_index_flag flags, - enum language lang, - const char *name, - cooked_index_entry_ref parent_entry, - dwarf2_per_cu *per_cu) -{ - if (tag == DW_TAG_module || tag == DW_TAG_namespace) - flags &= ~IS_STATIC; - else if (lang == language_cplus - && (tag == DW_TAG_class_type - || tag == DW_TAG_interface_type - || tag == DW_TAG_structure_type - || tag == DW_TAG_union_type - || tag == DW_TAG_enumeration_type - || tag == DW_TAG_enumerator)) - flags &= ~IS_STATIC; - else if (tag_is_type (tag)) - flags |= IS_STATIC; - - return new (&m_storage) cooked_index_entry (die_offset, tag, flags, - lang, name, parent_entry, - per_cu); -} - -/* See cooked-index.h. */ - -cooked_index_entry * -cooked_index_shard::add (sect_offset die_offset, enum dwarf_tag tag, - cooked_index_flag flags, enum language lang, - const char *name, cooked_index_entry_ref parent_entry, - dwarf2_per_cu *per_cu) -{ - cooked_index_entry *result = create (die_offset, tag, flags, lang, name, - parent_entry, per_cu); - m_entries.push_back (result); - - /* An explicitly-tagged main program should always override the - implicit "main" discovery. */ - if ((flags & IS_MAIN) != 0) - m_main = result; - else if ((flags & IS_PARENT_DEFERRED) == 0 - && parent_entry.resolved == nullptr - && m_main == nullptr - && language_may_use_plain_main (lang) - && strcmp (name, "main") == 0) - m_main = result; - - return result; -} - -/* See cooked-index.h. */ - -void -cooked_index_shard::handle_gnat_encoded_entry - (cooked_index_entry *entry, - htab_t gnat_entries, - std::vector<cooked_index_entry *> &new_entries) -{ - /* We decode Ada names in a particular way: operators and wide - characters are left as-is. This is done to make name matching a - bit simpler; and for wide characters, it means the choice of Ada - source charset does not affect the indexer directly. */ - std::string canonical = ada_decode (entry->name, false, false, false); - if (canonical.empty ()) - { - entry->canonical = entry->name; - return; - } - std::vector<std::string_view> names = split_name (canonical.c_str (), - split_style::DOT_STYLE); - std::string_view tail = names.back (); - names.pop_back (); - - const cooked_index_entry *parent = nullptr; - for (const auto &name : names) - { - uint32_t hashval = dwarf5_djb_hash (name); - void **slot = htab_find_slot_with_hash (gnat_entries, &name, - hashval, INSERT); - /* CUs are processed in order, so we only need to check the most - recent entry. */ - cooked_index_entry *last = (cooked_index_entry *) *slot; - if (last == nullptr || last->per_cu != entry->per_cu) - { - const char *new_name = m_names.insert (name); - last = create (entry->die_offset, DW_TAG_module, - IS_SYNTHESIZED, language_ada, new_name, parent, - entry->per_cu); - last->canonical = last->name; - new_entries.push_back (last); - *slot = last; - } - - parent = last; - } - - entry->set_parent (parent); - entry->canonical = m_names.insert (tail); -} - -/* Hash a cooked index entry by name pointer value. - - We can use pointer equality here because names come from .debug_str, which - will normally be unique-ified by the linker. Also, duplicates are relatively - harmless -- they just mean a bit of extra memory is used. */ - -struct cooked_index_entry_name_ptr_hash -{ - using is_avalanching = void; - - std::uint64_t operator () (const cooked_index_entry *entry) const noexcept - { - return ankerl::unordered_dense::hash<const char *> () (entry->name); - } -}; - -/* Compare cooked index entries by name pointer value. */ - -struct cooked_index_entry_name_ptr_eq -{ - bool operator () (const cooked_index_entry *a, - const cooked_index_entry *b) const noexcept - { - return a->name == b->name; - } -}; - -/* See cooked-index.h. */ - -void -cooked_index_shard::finalize (const parent_map_map *parent_maps) -{ - gdb::unordered_set<const cooked_index_entry *, - cooked_index_entry_name_ptr_hash, - cooked_index_entry_name_ptr_eq> seen_names; - - auto hash_entry = [] (const void *e) - { - const cooked_index_entry *entry = (const cooked_index_entry *) e; - return dwarf5_djb_hash (entry->canonical); - }; - - auto eq_entry = [] (const void *a, const void *b) -> int - { - const cooked_index_entry *ae = (const cooked_index_entry *) a; - const std::string_view *sv = (const std::string_view *) b; - return (strlen (ae->canonical) == sv->length () - && strncasecmp (ae->canonical, sv->data (), sv->length ()) == 0); - }; - - htab_up gnat_entries (htab_create_alloc (10, hash_entry, eq_entry, - nullptr, xcalloc, xfree)); - std::vector<cooked_index_entry *> new_gnat_entries; - - for (cooked_index_entry *entry : m_entries) - { - if ((entry->flags & IS_PARENT_DEFERRED) != 0) - { - const cooked_index_entry *new_parent - = parent_maps->find (entry->get_deferred_parent ()); - entry->resolve_parent (new_parent); - } - - /* Note that this code must be kept in sync with - language_requires_canonicalization. */ - gdb_assert (entry->canonical == nullptr); - if ((entry->flags & IS_LINKAGE) != 0) - entry->canonical = entry->name; - else if (entry->lang == language_ada) - { - /* Newer versions of GNAT emit DW_TAG_module and use a - hierarchical structure. In this case, we don't need to - do any extra work. This can be detected by looking for a - GNAT-encoded name. */ - if (strstr (entry->name, "__") == nullptr) - { - entry->canonical = entry->name; - - /* If the entry does not have a parent, then there's - nothing extra to do here -- the entry itself is - sufficient. - - However, if it does have a parent, we have to - synthesize an entry with the full name. This is - unfortunate, but it's necessary due to how some of - the Ada name-lookup code currently works. For - example, without this, ada_get_tsd_type will - fail. - - Eventually it would be good to change the Ada lookup - code, and then remove these entries (and supporting - code in cooked_index_entry::full_name). */ - if (entry->get_parent () != nullptr) - { - const char *fullname - = entry->full_name (&m_storage, FOR_ADA_LINKAGE_NAME); - cooked_index_entry *linkage = create (entry->die_offset, - entry->tag, - (entry->flags - | IS_LINKAGE - | IS_SYNTHESIZED), - language_ada, - fullname, - nullptr, - entry->per_cu); - linkage->canonical = fullname; - new_gnat_entries.push_back (linkage); - } - } - else - handle_gnat_encoded_entry (entry, gnat_entries.get (), - new_gnat_entries); - } - else if (entry->lang == language_cplus || entry->lang == language_c) - { - auto [it, inserted] = seen_names.insert (entry); - - if (inserted) - { - /* No entry with that name was present, compute the canonical - name. */ - gdb::unique_xmalloc_ptr<char> canon_name - = (entry->lang == language_cplus - ? cp_canonicalize_string (entry->name) - : c_canonicalize_name (entry->name)); - if (canon_name == nullptr) - entry->canonical = entry->name; - else - entry->canonical = m_names.insert (std::move (canon_name)); - } - else - { - /* An entry with that name was present, re-use its canonical - name. */ - entry->canonical = (*it)->canonical; - } - } - else - entry->canonical = entry->name; - } - - /* Make sure any new Ada entries end up in the results. This isn't - done when creating these new entries to avoid invalidating the - m_entries iterator used in the foreach above. */ - m_entries.insert (m_entries.end (), new_gnat_entries.begin (), - new_gnat_entries.end ()); - - m_entries.shrink_to_fit (); - std::sort (m_entries.begin (), m_entries.end (), - [] (const cooked_index_entry *a, const cooked_index_entry *b) - { - return *a < *b; - }); -} - -/* See cooked-index.h. */ - -cooked_index_shard::range -cooked_index_shard::find (const std::string &name, bool completing) const -{ - cooked_index_entry::comparison_mode mode = (completing - ? cooked_index_entry::COMPLETE - : cooked_index_entry::MATCH); - - auto lower = std::lower_bound (m_entries.cbegin (), m_entries.cend (), name, - [=] (const cooked_index_entry *entry, - const std::string &n) - { - return cooked_index_entry::compare (entry->canonical, n.c_str (), mode) < 0; - }); - - auto upper = std::upper_bound (m_entries.cbegin (), m_entries.cend (), name, - [=] (const std::string &n, - const cooked_index_entry *entry) - { - return cooked_index_entry::compare (entry->canonical, n.c_str (), mode) > 0; - }); - - return range (lower, upper); -} - -/* See cooked-index.h. */ - -void -cooked_index_worker::start () -{ - gdb::thread_pool::g_thread_pool->post_task ([this] () - { - try - { - do_reading (); - } - catch (const gdb_exception &exc) - { - m_failed = exc; - set (cooked_state::CACHE_DONE); - } - - bfd_thread_cleanup (); - }); -} - -/* See cooked-index.h. */ - -bool -cooked_index_worker::wait (cooked_state desired_state, bool allow_quit) -{ - bool done; -#if CXX_STD_THREAD - { - std::unique_lock<std::mutex> lock (m_mutex); - - /* This may be called from a non-main thread -- this functionality - is needed for the index cache -- but in this case we require - that the desired state already have been attained. */ - gdb_assert (is_main_thread () || desired_state <= m_state); - - while (desired_state > m_state) - { - if (allow_quit) - { - std::chrono::milliseconds duration { 15 }; - if (m_cond.wait_for (lock, duration) == std::cv_status::timeout) - QUIT; - } - else - m_cond.wait (lock); - } - done = m_state == cooked_state::CACHE_DONE; - } -#else - /* Without threads, all the work is done immediately on the main - thread, and there is never anything to wait for. */ - done = desired_state == cooked_state::CACHE_DONE; -#endif /* CXX_STD_THREAD */ - - /* Only the main thread is allowed to report complaints and the - like. */ - if (!is_main_thread ()) - return false; - - if (m_reported) - return done; - m_reported = true; - - /* Emit warnings first, maybe they were emitted before an exception - (if any) was thrown. */ - m_warnings.emit (); - - if (m_failed.has_value ()) - { - /* do_reading failed -- report it. */ - exception_print (gdb_stderr, *m_failed); - m_failed.reset (); - return done; - } - - /* Only show a given exception a single time. */ - gdb::unordered_set<gdb_exception> seen_exceptions; - for (auto &one_result : m_results) - { - re_emit_complaints (std::get<1> (one_result)); - for (auto &one_exc : std::get<2> (one_result)) - if (seen_exceptions.insert (one_exc).second) - exception_print (gdb_stderr, one_exc); - } - - print_stats (); - - struct objfile *objfile = m_per_objfile->objfile; - dwarf2_per_bfd *per_bfd = m_per_objfile->per_bfd; - cooked_index *table - = (gdb::checked_static_cast<cooked_index *> - (per_bfd->index_table.get ())); - - auto_obstack temp_storage; - enum language lang = language_unknown; - const char *main_name = table->get_main_name (&temp_storage, &lang); - if (main_name != nullptr) - set_objfile_main_name (objfile, main_name, lang); - - /* dwarf_read_debug_printf ("Done building psymtabs of %s", */ - /* objfile_name (objfile)); */ - - return done; -} - -/* See cooked-index.h. */ - -void -cooked_index_worker::set (cooked_state desired_state) -{ - gdb_assert (desired_state != cooked_state::INITIAL); - -#if CXX_STD_THREAD - std::lock_guard<std::mutex> guard (m_mutex); - gdb_assert (desired_state > m_state); - m_state = desired_state; - m_cond.notify_one (); -#else - /* Without threads, all the work is done immediately on the main - thread, and there is never anything to do. */ -#endif /* CXX_STD_THREAD */ -} - -/* See cooked-index.h. */ - -void -cooked_index_worker::write_to_cache (const cooked_index *idx, - deferred_warnings *warn) const -{ - if (idx != nullptr) - { - /* Writing to the index cache may cause a warning to be emitted. - See PR symtab/30837. This arranges to capture all such - warnings. This is safe because we know the deferred_warnings - object isn't in use by any other thread at this point. */ - scoped_restore_warning_hook defer (warn); - m_cache_store.store (); - } -} - cooked_index::cooked_index (cooked_index_worker_up &&worker) : m_state (std::move (worker)) { @@ -994,10 +330,6 @@ void _initialize_cooked_index (); void _initialize_cooked_index () { -#if GDB_SELF_TEST - selftests::register_test ("cooked_index_entry::compare", test_compare); -#endif - add_cmd ("wait-for-index-cache", class_maintenance, maintenance_wait_for_index_cache, _("\ Wait until all pending writes to the index cache have completed.\n\ diff --git a/gdb/dwarf2/cooked-index.h b/gdb/dwarf2/cooked-index.h index 56c84bd..c16afa0 100644 --- a/gdb/dwarf2/cooked-index.h +++ b/gdb/dwarf2/cooked-index.h @@ -21,489 +21,20 @@ #define GDB_DWARF2_COOKED_INDEX_H #include "dwarf2.h" +#include "dwarf2/cooked-index-entry.h" #include "dwarf2/types.h" #include "symtab.h" #include "hashtab.h" #include "quick-symbol.h" #include "gdbsupport/gdb_obstack.h" #include "addrmap.h" -#include "gdbsupport/iterator-range.h" #include "dwarf2/mapped-index.h" #include "dwarf2/read.h" #include "dwarf2/parent-map.h" #include "gdbsupport/range-chain.h" -#include "gdbsupport/string-set.h" #include "complaints.h" - -#if CXX_STD_THREAD -#include <mutex> -#include <condition_variable> -#endif /* CXX_STD_THREAD */ - -struct dwarf2_per_cu; -struct dwarf2_per_bfd; -struct index_cache_store_context; -struct cooked_index_entry; - -/* Flags that describe an entry in the index. */ -enum cooked_index_flag_enum : unsigned char -{ - /* True if this entry is the program's "main". */ - IS_MAIN = 1, - /* True if this entry represents a "static" object. */ - IS_STATIC = 2, - /* True if this entry uses the linkage name. */ - IS_LINKAGE = 4, - /* True if this entry is just for the declaration of a type, not the - definition. */ - IS_TYPE_DECLARATION = 8, - /* True is parent_entry.deferred has a value rather than parent_entry - .resolved. */ - IS_PARENT_DEFERRED = 16, - /* True if this entry was synthesized by gdb (as opposed to coming - directly from the DWARF). */ - IS_SYNTHESIZED = 32, -}; -DEF_ENUM_FLAGS_TYPE (enum cooked_index_flag_enum, cooked_index_flag); - -/* Flags used when requesting the full name of an entry. */ -enum cooked_index_full_name_enum : unsigned char -{ - /* Set when requesting the name of "main". See the method for the - full description. */ - FOR_MAIN = 1, - /* Set when requesting the linkage name for an Ada entry. */ - FOR_ADA_LINKAGE_NAME = 2, -}; -DEF_ENUM_FLAGS_TYPE (enum cooked_index_full_name_enum, cooked_index_full_name_flag); - -/* Type representing either a resolved or deferred cooked_index_entry. */ - -union cooked_index_entry_ref -{ - cooked_index_entry_ref (parent_map::addr_type deferred_) - { - deferred = deferred_; - } - - cooked_index_entry_ref (const cooked_index_entry *resolved_) - { - resolved = resolved_; - } - - const cooked_index_entry *resolved; - parent_map::addr_type deferred; -}; - -/* Return a string representation of FLAGS. */ - -std::string to_string (cooked_index_flag flags); - -/* Return true if LANG requires canonicalization. This is used - primarily to work around an issue computing the name of "main". - This function must be kept in sync with - cooked_index_shard::finalize. */ - -extern bool language_requires_canonicalization (enum language lang); - -/* A cooked_index_entry represents a single item in the index. Note - that two entries can be created for the same DIE -- one using the - name, and another one using the linkage name, if any. - - This is an "open" class and the members are all directly - accessible. It is read-only after the index has been fully read - and processed. */ -struct cooked_index_entry : public allocate_on_obstack<cooked_index_entry> -{ - cooked_index_entry (sect_offset die_offset_, enum dwarf_tag tag_, - cooked_index_flag flags_, - enum language lang_, const char *name_, - cooked_index_entry_ref parent_entry_, - dwarf2_per_cu *per_cu_) - : name (name_), - tag (tag_), - flags (flags_), - lang (lang_), - die_offset (die_offset_), - per_cu (per_cu_), - m_parent_entry (parent_entry_) - { - } - - /* Return true if this entry matches SEARCH_FLAGS. */ - bool matches (block_search_flags search_flags) const - { - /* Just reject type declarations. */ - if ((flags & IS_TYPE_DECLARATION) != 0) - return false; - - if ((search_flags & SEARCH_STATIC_BLOCK) != 0 - && (flags & IS_STATIC) != 0) - return true; - if ((search_flags & SEARCH_GLOBAL_BLOCK) != 0 - && (flags & IS_STATIC) == 0) - return true; - return false; - } - - /* Return true if this entry matches KIND. */ - bool matches (domain_search_flags kind) const; - - /* Construct the fully-qualified name of this entry and return a - pointer to it. If allocation is needed, it will be done on - STORAGE. - - FLAGS affects the result. If the FOR_MAIN flag is set, we are - computing the name of the "main" entry -- one marked - DW_AT_main_subprogram. This matters for avoiding name - canonicalization and also a related race (if "main" computation - is done during finalization). - - If the FOR_ADA_LINKAGE_NAME flag is set, then Ada-language - symbols will have their "linkage-style" name computed. The - default is source-style. - - If the language doesn't prescribe a separator, one can be - specified using DEFAULT_SEP. */ - const char *full_name (struct obstack *storage, - cooked_index_full_name_flag name_flags = 0, - const char *default_sep = nullptr) const; - - /* Comparison modes for the 'compare' function. See the function - for a description. */ - enum comparison_mode - { - MATCH, - SORT, - COMPLETE, - }; - - /* Compare two strings, case-insensitively. Return -1 if STRA is - less than STRB, 0 if they are equal, and 1 if STRA is greater. - - When comparing, '<' is considered to be less than all other - printable characters. This ensures that "t<x>" sorts before - "t1", which is necessary when looking up "t". This '<' handling - is to ensure that certain C++ lookups work correctly. It is - inexact, and applied regardless of the search language, but this - is ok because callers of this code do more precise filtering - according to their needs. This is also why using a - case-insensitive comparison works even for languages that are - case sensitive. - - MODE controls how the comparison proceeds. - - MODE==SORT is used when sorting and the only special '<' handling - that it does is to ensure that '<' sorts before all other - printable characters. This ensures that the resulting ordering - will be binary-searchable. - - MODE==MATCH is used when searching for a symbol. In this case, - STRB must always be the search name, and STRA must be the name in - the index that is under consideration. In compare mode, early - termination of STRB may match STRA -- for example, "t<int>" and - "t" will be considered to be equal. (However, if A=="t" and - B=="t<int>", then this will not consider them as equal.) - - MODE==COMPLETE is used when searching for a symbol for - completion. In this case, STRB must always be the search name, - and STRA must be the name in the index that is under - consideration. In completion mode, early termination of STRB - always results in a match. */ - static int compare (const char *stra, const char *strb, - comparison_mode mode); - - /* Compare two entries by canonical name. */ - bool operator< (const cooked_index_entry &other) const - { - return compare (canonical, other.canonical, SORT) < 0; - } - - /* Set parent entry to PARENT. */ - void set_parent (const cooked_index_entry *parent) - { - gdb_assert ((flags & IS_PARENT_DEFERRED) == 0); - m_parent_entry.resolved = parent; - } - - /* Resolve deferred parent entry to PARENT. */ - void resolve_parent (const cooked_index_entry *parent) - { - gdb_assert ((flags & IS_PARENT_DEFERRED) != 0); - flags = flags & ~IS_PARENT_DEFERRED; - m_parent_entry.resolved = parent; - } - - /* Return parent entry. */ - const cooked_index_entry *get_parent () const - { - gdb_assert ((flags & IS_PARENT_DEFERRED) == 0); - return m_parent_entry.resolved; - } - - /* Return deferred parent entry. */ - parent_map::addr_type get_deferred_parent () const - { - gdb_assert ((flags & IS_PARENT_DEFERRED) != 0); - return m_parent_entry.deferred; - } - - /* 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 - attributes. */ - const char *name; - /* The canonical name. This may be equal to NAME. */ - const char *canonical = nullptr; - /* The DWARF tag. */ - 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 offset of this DIE. */ - sect_offset die_offset; - /* The CU from which this entry originates. */ - dwarf2_per_cu *per_cu; - -private: - - /* A helper method for full_name. Emits the full scope of this - object, followed by the separator, to STORAGE. If this entry has - a parent, its write_scope method is called first. See full_name - for a description of the FLAGS parameter. */ - void write_scope (struct obstack *storage, const char *sep, - cooked_index_full_name_flag flags) const; - - /* The parent entry. This is NULL for top-level entries. - Otherwise, it points to the parent entry, such as a namespace or - class. */ - cooked_index_entry_ref m_parent_entry; -}; - -class cooked_index; - -/* An index of interesting DIEs. This is "cooked", in contrast to a - mapped .debug_names or .gdb_index, which are "raw". An entry in - the index is of type cooked_index_entry. - - Operations on the index are described below. They are chosen to - make it relatively simple to implement the symtab "quick" - methods. */ -class cooked_index_shard -{ -public: - cooked_index_shard () = default; - DISABLE_COPY_AND_ASSIGN (cooked_index_shard); - - /* Create a new cooked_index_entry and register it with this object. - Entries are owned by this object. The new item is returned. */ - cooked_index_entry *add (sect_offset die_offset, enum dwarf_tag tag, - cooked_index_flag flags, enum language lang, - const char *name, - cooked_index_entry_ref parent_entry, - dwarf2_per_cu *per_cu); - - /* Install a new fixed addrmap from the given mutable addrmap. */ - void install_addrmap (addrmap_mutable *map) - { - gdb_assert (m_addrmap == nullptr); - m_addrmap = new (&m_storage) addrmap_fixed (&m_storage, map); - } - - friend class cooked_index; - - /* A simple range over part of m_entries. */ - typedef iterator_range<std::vector<cooked_index_entry *>::const_iterator> - range; - - /* Return a range of all the entries. */ - range all_entries () const - { - return { m_entries.cbegin (), m_entries.cend () }; - } - - /* 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) const; - -private: - - /* 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 - { - return m_main; - } - - /* 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) - { - if (m_addrmap == nullptr) - return nullptr; - - return (static_cast<dwarf2_per_cu *> (m_addrmap->find ((CORE_ADDR) addr))); - } - - /* Create a new cooked_index_entry and register it with this object. - Entries are owned by this object. The new item is returned. */ - cooked_index_entry *create (sect_offset die_offset, - enum dwarf_tag tag, - cooked_index_flag flags, - enum language lang, - const char *name, - cooked_index_entry_ref parent_entry, - dwarf2_per_cu *per_cu); - - /* When GNAT emits mangled ("encoded") names in the DWARF, and does - not emit the module structure, we still need this structuring to - do lookups. This function recreates that information for an - existing entry, modifying ENTRY as appropriate. Any new entries - are added to NEW_ENTRIES. */ - void handle_gnat_encoded_entry - (cooked_index_entry *entry, htab_t gnat_entries, - std::vector<cooked_index_entry *> &new_entries); - - /* Finalize the index. This should be called a single time, when - the index has been fully populated. It enters all the entries - into the internal table and fixes up all missing parent links. - This may be invoked in a worker thread. */ - void finalize (const parent_map_map *parent_maps); - - /* Storage for the entries. */ - auto_obstack m_storage; - /* List of all entries. */ - std::vector<cooked_index_entry *> m_entries; - /* If we found an entry with 'is_main' set, store it here. */ - cooked_index_entry *m_main = nullptr; - /* The addrmap. This maps address ranges to dwarf2_per_cu objects. */ - addrmap_fixed *m_addrmap = nullptr; - /* Storage for canonical names. */ - gdb::string_set m_names; -}; - -using cooked_index_shard_up = std::unique_ptr<cooked_index_shard>; - -/* The possible states of the index. See the explanatory comment - before cooked_index for more details. */ -enum class cooked_state -{ - /* The default state. This is not a valid argument to 'wait'. */ - INITIAL, - /* The initial scan has completed. The name of "main" is now - available (if known). The addrmaps are usable now. - Finalization has started but is not complete. */ - MAIN_AVAILABLE, - /* Finalization has completed. This means the index is fully - available for queries. */ - FINALIZED, - /* Writing to the index cache has finished. */ - CACHE_DONE, -}; - -/* An object of this type controls the scanning of the DWARF. It - schedules the worker tasks and tracks the current state. Once - scanning is done, this object is discarded. - - This is an abstract base class that defines the basic behavior of - scanners. Separate concrete implementations exist for scanning - .debug_names and .debug_info. */ - -class cooked_index_worker -{ -public: - - explicit cooked_index_worker (dwarf2_per_objfile *per_objfile) - : m_per_objfile (per_objfile), - m_cache_store (global_index_cache, per_objfile->per_bfd) - { } - virtual ~cooked_index_worker () - { } - DISABLE_COPY_AND_ASSIGN (cooked_index_worker); - - /* Start reading. */ - void start (); - - /* Wait for a particular state to be achieved. If ALLOW_QUIT is - true, then the loop will check the QUIT flag. Normally this - method may only be called from the main thread; however, it can - be called from a worker thread provided that the desired state - has already been attained. (This oddity is used by the index - cache writer.) */ - bool wait (cooked_state desired_state, bool allow_quit); - -protected: - - /* Let cooked_index call the 'set' and 'write_to_cache' methods. */ - friend class cooked_index; - - /* Set the current state. */ - void set (cooked_state desired_state); - - /* Write to the index cache. */ - void write_to_cache (const cooked_index *idx, - deferred_warnings *warn) const; - - /* Helper function that does the work of reading. This must be able - to be run in a worker thread without problems. */ - virtual void do_reading () = 0; - - /* A callback that can print stats, if needed. This is called when - transitioning to the 'MAIN_AVAILABLE' state. */ - virtual void print_stats () - { } - - /* Each thread returns a tuple holding a cooked index, any collected - complaints, a vector of errors that should be printed, and a - parent map. - - The errors are retained because GDB's I/O system is not - thread-safe. run_on_main_thread could be used, but that would - mean the messages are printed after the prompt, which looks - weird. */ - using result_type = std::tuple<cooked_index_shard_up, - complaint_collection, - std::vector<gdb_exception>, - parent_map>; - - /* The per-objfile object. */ - dwarf2_per_objfile *m_per_objfile; - /* Result of each worker task. */ - std::vector<result_type> m_results; - /* Any warnings emitted. This is not in 'result_type' because (for - the time being at least), it's only needed in do_reading, not in - every worker. Note that deferred_warnings uses gdb_stderr in its - constructor, and this should only be done from the main thread. - This is enforced in the cooked_index_worker constructor. */ - deferred_warnings m_warnings; - - /* A map of all parent maps. Used during finalization to fix up - parent relationships. */ - parent_map_map m_all_parents_map; - -#if CXX_STD_THREAD - /* Current state of this object. */ - cooked_state m_state = cooked_state::INITIAL; - /* Mutex and condition variable used to synchronize. */ - std::mutex m_mutex; - std::condition_variable m_cond; -#endif /* CXX_STD_THREAD */ - /* This flag indicates whether any complaints or exceptions that - arose during scanning have been reported by 'wait'. This may - only be modified on the main thread. */ - bool m_reported = false; - /* If set, an exception occurred during reading; in this case the - scanning is stopped and this exception will later be reported by - the 'wait' method. */ - std::optional<gdb_exception> m_failed; - /* An object used to write to the index cache. */ - index_cache_store_context m_cache_store; -}; - -using cooked_index_worker_up = std::unique_ptr<cooked_index_worker>; +#include "dwarf2/cooked-index-shard.h" +#include "dwarf2/cooked-index-worker.h" /* The main index of DIEs. @@ -515,6 +46,14 @@ using cooked_index_worker_up = std::unique_ptr<cooked_index_worker>; 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 @@ -546,6 +85,7 @@ using cooked_index_worker_up = std::unique_ptr<cooked_index_worker>; . v . maybe write to index cache . state = CACHE_DONE + . ~cooked_index_worker . . . if main thread calls... diff --git a/gdb/dwarf2/cooked-indexer.c b/gdb/dwarf2/cooked-indexer.c index 3b80cd6..1f3a235 100644 --- a/gdb/dwarf2/cooked-indexer.c +++ b/gdb/dwarf2/cooked-indexer.c @@ -18,12 +18,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "dwarf2/cooked-indexer.h" -#include "dwarf2/cooked-index-storage.h" +#include "dwarf2/cooked-index-worker.h" #include "dwarf2/error.h" /* See cooked-indexer.h. */ -cooked_indexer::cooked_indexer (cooked_index_storage *storage, +cooked_indexer::cooked_indexer (cooked_index_worker_result *storage, dwarf2_per_cu *per_cu, enum language language) : m_index_storage (storage), m_per_cu (per_cu), @@ -109,7 +109,7 @@ cooked_indexer::ensure_cu_exists (cutu_reader *reader, cutu_reader *result = m_index_storage->get_reader (per_cu); if (result == nullptr) { - cutu_reader new_reader (per_cu, per_objfile, nullptr, nullptr, false, + cutu_reader new_reader (*per_cu, *per_objfile, nullptr, nullptr, false, language_minimal, &m_index_storage->get_abbrev_table_cache ()); diff --git a/gdb/dwarf2/cooked-indexer.h b/gdb/dwarf2/cooked-indexer.h index 93626a9..99e9fdd 100644 --- a/gdb/dwarf2/cooked-indexer.h +++ b/gdb/dwarf2/cooked-indexer.h @@ -26,7 +26,7 @@ #include <variant> struct abbrev_info; -struct cooked_index_storage; +struct cooked_index_worker_result; struct cutu_reader; struct dwarf2_per_cu; struct dwarf2_per_objfile; @@ -36,7 +36,7 @@ struct dwarf2_per_objfile; class cooked_indexer { public: - cooked_indexer (cooked_index_storage *storage, dwarf2_per_cu *per_cu, + cooked_indexer (cooked_index_worker_result *storage, dwarf2_per_cu *per_cu, enum language language); DISABLE_COPY_AND_ASSIGN (cooked_indexer); @@ -103,7 +103,7 @@ private: bool fully); /* The storage object, where the results are kept. */ - cooked_index_storage *m_index_storage; + 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 diff --git a/gdb/dwarf2/die.h b/gdb/dwarf2/die.h index 41ed600..cffb5cb 100644 --- a/gdb/dwarf2/die.h +++ b/gdb/dwarf2/die.h @@ -22,6 +22,7 @@ #include "complaints.h" #include "dwarf2/attribute.h" +#include "gdbsupport/next-iterator.h" /* This data structure holds a complete die structure. */ struct die_info @@ -103,6 +104,13 @@ struct die_info return 0; } + /* Return a range suitable for iterating over the children of this + DIE. */ + next_range<die_info> children () const + { + return next_range<die_info> (child); + } + /* DWARF-2 tag for this DIE. */ ENUM_BITFIELD(dwarf_tag) tag : 16; diff --git a/gdb/dwarf2/loc.c b/gdb/dwarf2/loc.c index 7c12c0d..8f66694 100644 --- a/gdb/dwarf2/loc.c +++ b/gdb/dwarf2/loc.c @@ -1784,6 +1784,7 @@ dwarf2_compile_property_to_c (string_file *stream, CORE_ADDR pc, struct symbol *sym) { +#if defined (HAVE_COMPILE) const dwarf2_property_baton *baton = prop->baton (); const gdb_byte *data; size_t size; @@ -1810,6 +1811,9 @@ dwarf2_compile_property_to_c (string_file *stream, gdbarch, registers_used, per_cu->addr_size (), data, data + size, per_cu, per_objfile); +#else + gdb_assert_not_reached ("Compile support was disabled"); +#endif } /* Compute the correct symbol_needs_kind value for the location @@ -3852,6 +3856,7 @@ locexpr_generate_c_location (struct symbol *sym, string_file *stream, std::vector<bool> ®isters_used, CORE_ADDR pc, const char *result_name) { +#if defined (HAVE_COMPILE) struct dwarf2_locexpr_baton *dlbaton = (struct dwarf2_locexpr_baton *) SYMBOL_LOCATION_BATON (sym); unsigned int addr_size = dlbaton->per_cu->addr_size (); @@ -3863,6 +3868,9 @@ locexpr_generate_c_location (struct symbol *sym, string_file *stream, sym, pc, gdbarch, registers_used, addr_size, dlbaton->data, dlbaton->data + dlbaton->size, dlbaton->per_cu, dlbaton->per_objfile); +#else + gdb_assert_not_reached ("Compile support was disabled"); +#endif } /* The set of location functions used with the DWARF-2 expression @@ -4088,6 +4096,7 @@ loclist_generate_c_location (struct symbol *sym, string_file *stream, std::vector<bool> ®isters_used, CORE_ADDR pc, const char *result_name) { +#if defined (HAVE_COMPILE) struct dwarf2_loclist_baton *dlbaton = (struct dwarf2_loclist_baton *) SYMBOL_LOCATION_BATON (sym); unsigned int addr_size = dlbaton->per_cu->addr_size (); @@ -4103,6 +4112,9 @@ loclist_generate_c_location (struct symbol *sym, string_file *stream, data, data + size, dlbaton->per_cu, dlbaton->per_objfile); +#else + gdb_assert_not_reached ("Compile support was disabled"); +#endif } /* The set of location functions used with the DWARF-2 expression diff --git a/gdb/dwarf2/read-debug-names.c b/gdb/dwarf2/read-debug-names.c index edac713..96a8ad3 100644 --- a/gdb/dwarf2/read-debug-names.c +++ b/gdb/dwarf2/read-debug-names.c @@ -114,11 +114,12 @@ struct mapped_debug_names_reader gdb::unordered_map<ULONGEST, index_val> abbrev_map; - /* Even though the scanning of .debug_names and creation of the cooked index - entries is done serially, we create multiple shards so that the - finalization step can be parallelized. The shards are filled in a round - robin fashion. */ - std::vector<cooked_index_shard_up> shards; + /* Even though the scanning of .debug_names and creation of the + cooked index entries is done serially, we create multiple shards + so that the finalization step can be parallelized. The shards + are filled in a round robin fashion. It's convenient to use a + result object rather than an actual shard. */ + std::vector<cooked_index_worker_result> indices; /* Next shard to insert an entry in. */ int next_shard = 0; @@ -290,11 +291,11 @@ mapped_debug_names_reader::scan_one_entry (const char *name, if (per_cu != nullptr) { *result - = shards[next_shard]->add (die_offset, (dwarf_tag) indexval.dwarf_tag, + = indices[next_shard].add (die_offset, (dwarf_tag) indexval.dwarf_tag, flags, lang, name, nullptr, per_cu); ++next_shard; - if (next_shard == shards.size ()) + if (next_shard == indices.size ()) next_shard = 0; entry_pool_offsets_to_entries.emplace (offset_in_entry_pool, *result); @@ -414,29 +415,31 @@ void cooked_index_worker_debug_names::do_reading () { complaint_interceptor complaint_handler; - std::vector<gdb_exception> exceptions; + try { m_map.scan_all_names (); } - catch (const gdb_exception &exc) + catch (gdb_exception &exc) { - exceptions.push_back (std::move (exc)); + /* Arbitrarily put all exceptions into the first result. */ + m_map.indices[0].note_error (std::move (exc)); } - m_results.emplace_back (nullptr, - complaint_handler.release (), - std::move (exceptions), - parent_map ()); - - dwarf2_per_bfd *per_bfd = m_per_objfile->per_bfd; - cooked_index *table - = (gdb::checked_static_cast<cooked_index *> - (per_bfd->index_table.get ())); + bool first = true; + for (auto &iter : m_map.indices) + { + if (first) + { + iter.done_reading (complaint_handler.release ()); + first = false; + } + else + iter.done_reading ({}); + } - /* Note that this code never uses IS_PARENT_DEFERRED, so it is safe - to pass nullptr here. */ - table->set_contents (std::move (m_map.shards), &m_warnings, nullptr); + m_results = std::move (m_map.indices); + done_reading (); bfd_thread_cleanup (); } @@ -838,24 +841,26 @@ do_dwarf2_read_debug_names (dwarf2_per_objfile *per_objfile) } per_bfd->debug_aranges.read (per_objfile->objfile); - addrmap_mutable addrmap; + + /* There is a single address map for the whole index (coming from + .debug_aranges). We only need to install it into a single shard + for it to get searched by cooked_index. So, we make the first + result object here, so we can store the addrmap, then move it + into place later. */ + cooked_index_worker_result first; deferred_warnings warnings; read_addrmap_from_aranges (per_objfile, &per_bfd->debug_aranges, - &addrmap, &warnings); + first.get_addrmap (), &warnings); warnings.emit (); const auto n_workers = std::max<std::size_t> (gdb::thread_pool::g_thread_pool->thread_count (), 1); - /* Create as many index shard as there are worker threads. */ - for (int i = 0; i < n_workers; ++i) - map.shards.emplace_back (std::make_unique<cooked_index_shard> ()); - - /* There is a single address map for the whole index (coming from - .debug_aranges). We only need to install it into a single shard for it to - get searched by cooked_index. */ - map.shards[0]->install_addrmap (&addrmap); + /* Create as many index shard as there are worker threads, + preserving the first one. */ + map.indices.push_back (std::move (first)); + map.indices.resize (n_workers); auto cidn = (std::make_unique<cooked_index_worker_debug_names> (per_objfile, std::move (map))); diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c index 77f6c38..17f06ff 100644 --- a/gdb/dwarf2/read.c +++ b/gdb/dwarf2/read.c @@ -32,7 +32,7 @@ #include "dwarf2/aranges.h" #include "dwarf2/attribute.h" #include "dwarf2/comp-unit-head.h" -#include "dwarf2/cooked-index-storage.h" +#include "dwarf2/cooked-index-worker.h" #include "dwarf2/cooked-indexer.h" #include "dwarf2/cu.h" #include "dwarf2/index-cache.h" @@ -728,7 +728,7 @@ show_dwarf_synchronous (struct ui_file *file, int from_tty, /* local function prototypes */ static void build_type_psymtabs_reader (cutu_reader *reader, - cooked_index_storage *storage); + cooked_index_worker_result *storage); static void var_decode_location (struct attribute *attr, struct symbol *sym, @@ -1791,7 +1791,7 @@ dw2_get_file_names (dwarf2_per_cu *this_cu, dwarf2_per_objfile *per_objfile) if (this_cu->files_read) return this_cu->file_names; - cutu_reader reader (this_cu, per_objfile, nullptr, + cutu_reader reader (*this_cu, *per_objfile, nullptr, per_objfile->get_cu (this_cu), true, language_minimal, nullptr); if (!reader.is_dummy ()) @@ -2710,8 +2710,7 @@ cutu_reader::init_cu_die_reader (dwarf2_cu *cu, dwarf2_section_info *section, void cutu_reader::read_cutu_die_from_dwo (dwarf2_cu *cu, dwo_unit *dwo_unit, die_info *stub_comp_unit_die, - const char *stub_comp_dir, - abbrev_table_up *result_dwo_abbrev_table) + const char *stub_comp_dir) { dwarf2_per_objfile *per_objfile = cu->per_objfile; dwarf2_per_cu *per_cu = cu->per_cu; @@ -2829,10 +2828,18 @@ cutu_reader::read_cutu_die_from_dwo (dwarf2_cu *cu, dwo_unit *dwo_unit, } dwo_abbrev_section->read (objfile); - *result_dwo_abbrev_table + m_dwo_abbrev_table = abbrev_table::read (dwo_abbrev_section, cu->header.abbrev_sect_off); this->init_cu_die_reader (cu, section, dwo_unit->dwo_file, - result_dwo_abbrev_table->get ()); + m_dwo_abbrev_table.get ()); + + /* Skip dummy compilation units. */ + if (m_info_ptr >= begin_info_ptr + dwo_unit->length + || peek_abbrev_code (abfd, m_info_ptr) == 0) + { + m_dummy_p = true; + return; + } /* Read in the die, filling in the attributes from the stub. This has the benefit of simplifying the rest of the code - all the @@ -2841,11 +2848,6 @@ cutu_reader::read_cutu_die_from_dwo (dwarf2_cu *cu, dwo_unit *dwo_unit, m_top_level_die = this->read_toplevel_die (gdb::make_array_view (attributes, next_attr_idx)); - - /* Skip dummy compilation units. */ - if (m_info_ptr >= begin_info_ptr + dwo_unit->length - || peek_abbrev_code (abfd, m_info_ptr) == 0) - m_dummy_p = true; } /* Return the signature of the compile unit, if found. In DWARF 4 and before, @@ -2864,8 +2866,12 @@ lookup_dwo_id (struct dwarf2_cu *cu, struct die_info* comp_unit_die) } /* Subroutine of cutu_reader to simplify it. - Look up the DWO unit specified by COMP_UNIT_DIE of THIS_CU. - Returns NULL if the specified DWO unit cannot be found. */ + Look up the DWO unit specified by COMP_UNIT_DIE of CU. + + DWO_NAME is the name (DW_AT_dwo_name) of the DWO unit already read from + COMP_UNIT_DIE. + + Returns nullptr if the specified DWO unit cannot be found. */ static struct dwo_unit * lookup_dwo_unit (dwarf2_cu *cu, die_info *comp_unit_die, const char *dwo_name) @@ -2883,8 +2889,6 @@ lookup_dwo_unit (dwarf2_cu *cu, die_info *comp_unit_die, const char *dwo_name) gdb_assert (cu != NULL); - /* Yeah, we look dwo_name up again, but it simplifies the code. */ - dwo_name = dwarf2_dwo_name (comp_unit_die, cu); comp_dir = dwarf2_string_attr (comp_unit_die, DW_AT_comp_dir, cu); if (per_cu->is_debug_types) @@ -2946,8 +2950,7 @@ cutu_reader::init_tu_and_read_dwo_dies (dwarf2_per_cu *this_cu, could share abbrev tables. */ read_cutu_die_from_dwo (cu, sig_type->dwo_unit, NULL /* stub_comp_unit_die */, - sig_type->dwo_unit->dwo_file->comp_dir, - &m_dwo_abbrev_table); + sig_type->dwo_unit->dwo_file->comp_dir); prepare_one_comp_unit (cu, pretend_language); } @@ -2961,16 +2964,16 @@ cutu_reader::init_tu_and_read_dwo_dies (dwarf2_per_cu *this_cu, If EXISTING_CU is non-NULL, then use it. Otherwise, a new CU is allocated. */ -cutu_reader::cutu_reader (dwarf2_per_cu *this_cu, - dwarf2_per_objfile *per_objfile, +cutu_reader::cutu_reader (dwarf2_per_cu &this_cu, + dwarf2_per_objfile &per_objfile, const struct abbrev_table *abbrev_table, dwarf2_cu *existing_cu, bool skip_partial, enum language pretend_language, const abbrev_table_cache *cache) { - struct objfile *objfile = per_objfile->objfile; - struct dwarf2_section_info *section = this_cu->section; + struct objfile *objfile = per_objfile.objfile; + struct dwarf2_section_info *section = this_cu.section; bfd *abfd = section->get_bfd_owner (); const gdb_byte *begin_info_ptr; struct signatured_type *sig_type = NULL; @@ -2982,17 +2985,17 @@ cutu_reader::cutu_reader (dwarf2_per_cu *this_cu, if (dwarf_die_debug) gdb_printf (gdb_stdlog, "Reading %s unit at offset %s\n", - this_cu->is_debug_types ? "type" : "comp", - sect_offset_str (this_cu->sect_off)); + this_cu.is_debug_types ? "type" : "comp", + sect_offset_str (this_cu.sect_off)); /* If we're reading a TU directly from a DWO file, including a virtual DWO file (instead of going through the stub), short-circuit all of this. */ - if (this_cu->reading_dwo_directly) + if (this_cu.reading_dwo_directly) { /* Narrow down the scope of possibilities to have to understand. */ - gdb_assert (this_cu->is_debug_types); + gdb_assert (this_cu.is_debug_types); gdb_assert (abbrev_table == NULL); - init_tu_and_read_dwo_dies (this_cu, per_objfile, existing_cu, + init_tu_and_read_dwo_dies (&this_cu, &per_objfile, existing_cu, pretend_language); return; } @@ -3001,9 +3004,9 @@ cutu_reader::cutu_reader (dwarf2_per_cu *this_cu, section->read (objfile); begin_info_ptr = m_info_ptr - = section->buffer + to_underlying (this_cu->sect_off); + = section->buffer + to_underlying (this_cu.sect_off); - abbrev_section = get_abbrev_section_for_cu (this_cu); + abbrev_section = get_abbrev_section_for_cu (&this_cu); dwarf2_cu *cu; @@ -3028,8 +3031,8 @@ cutu_reader::cutu_reader (dwarf2_per_cu *this_cu, is irrelevant, and (2) the get_cu method is not thread-safe. */ gdb_assert (cache != nullptr - || per_objfile->get_cu (this_cu) == nullptr); - m_new_cu = std::make_unique<dwarf2_cu> (this_cu, per_objfile); + || per_objfile.get_cu (&this_cu) == nullptr); + m_new_cu = std::make_unique<dwarf2_cu> (&this_cu, &per_objfile); cu = m_new_cu.get (); } @@ -3041,43 +3044,43 @@ cutu_reader::cutu_reader (dwarf2_per_cu *this_cu, } else { - if (this_cu->is_debug_types) + if (this_cu.is_debug_types) { m_info_ptr - = read_and_check_comp_unit_head (per_objfile, &cu->header, section, + = read_and_check_comp_unit_head (&per_objfile, &cu->header, section, abbrev_section, m_info_ptr, rcuh_kind::TYPE); /* Since per_cu is the first member of struct signatured_type, we can go from a pointer to one to a pointer to the other. */ - sig_type = (struct signatured_type *) this_cu; + sig_type = (struct signatured_type *) &this_cu; gdb_assert (sig_type->signature == cu->header.signature); gdb_assert (sig_type->type_offset_in_tu == cu->header.type_cu_offset_in_tu); - gdb_assert (this_cu->sect_off == cu->header.sect_off); + gdb_assert (this_cu.sect_off == cu->header.sect_off); /* LENGTH has not been set yet for type units if we're using .gdb_index. */ - this_cu->set_length (cu->header.get_length_with_initial ()); + this_cu.set_length (cu->header.get_length_with_initial ()); /* Establish the type offset that can be used to lookup the type. */ sig_type->type_offset_in_section = - this_cu->sect_off + to_underlying (sig_type->type_offset_in_tu); + this_cu.sect_off + to_underlying (sig_type->type_offset_in_tu); } else { m_info_ptr - = read_and_check_comp_unit_head (per_objfile, &cu->header, section, + = read_and_check_comp_unit_head (&per_objfile, &cu->header, section, abbrev_section, m_info_ptr, rcuh_kind::COMPILE); - gdb_assert (this_cu->sect_off == cu->header.sect_off); - this_cu->set_length (cu->header.get_length_with_initial ()); + gdb_assert (this_cu.sect_off == cu->header.sect_off); + this_cu.set_length (cu->header.get_length_with_initial ()); } } /* Skip dummy compilation units. */ - if (m_info_ptr >= begin_info_ptr + this_cu->length () + if (m_info_ptr >= begin_info_ptr + this_cu.length () || peek_abbrev_code (abfd, m_info_ptr) == 0) m_dummy_p = true; else @@ -3129,14 +3132,13 @@ cutu_reader::cutu_reader (dwarf2_per_cu *this_cu, { complaint (_("compilation unit with DW_AT_GNU_dwo_name" " has children (offset %s) [in module %s]"), - sect_offset_str (this_cu->sect_off), + sect_offset_str (this_cu.sect_off), bfd_get_filename (abfd)); } dwo_unit = lookup_dwo_unit (cu, m_top_level_die, dwo_name); if (dwo_unit != NULL) - read_cutu_die_from_dwo (cu, dwo_unit, m_top_level_die, nullptr, - &m_dwo_abbrev_table); + read_cutu_die_from_dwo (cu, dwo_unit, m_top_level_die, nullptr); else { /* Yikes, we couldn't find the rest of the DIE, we only have @@ -3165,65 +3167,57 @@ cutu_reader::release_cu () return std::move (m_new_cu); } -/* Read CU/TU THIS_CU but do not follow DW_AT_GNU_dwo_name (DW_AT_dwo_name) - if present. DWO_FILE, if non-NULL, is the DWO file to read (the caller is - assumed to have already done the lookup to find the DWO file). - - The caller is required to fill in THIS_CU->section, THIS_CU->offset, and - THIS_CU->is_debug_types, but nothing else. +/* This constructor exists for the special case of reading many units in a row + from a given known DWO file. - We fill in THIS_CU->length. + THIS_CU is a special dwarf2_per_cu to represent where to read the unit from, + in the DWO file. The caller is required to fill THIS_CU::SECTION, + THIS_CU::SECT_OFF, and THIS_CU::IS_DEBUG_TYPES. This constructor will fill + in the length. THIS_CU::SECTION must point to a section from the DWO file, + which is normally not the case for regular dwarf2_per_cu uses. - THIS_CU->cu is always freed when done. - This is done in order to not leave THIS_CU->cu in a state where we have - to care whether it refers to the "main" CU or the DWO CU. + PARENT_CU is the CU created when reading the skeleton unit, and is used to + provide a default value for str_offsets_base and addr_base. */ - When parent_cu is passed, it is used to provide a default value for - str_offsets_base and addr_base from the parent. */ - -cutu_reader::cutu_reader (dwarf2_per_cu *this_cu, - dwarf2_per_objfile *per_objfile, - enum language pretend_language, - struct dwarf2_cu *parent_cu, - struct dwo_file *dwo_file) +cutu_reader::cutu_reader (dwarf2_per_cu &this_cu, + dwarf2_per_objfile &per_objfile, + language pretend_language, dwarf2_cu &parent_cu, + dwo_file &dwo_file) { - struct objfile *objfile = per_objfile->objfile; - struct dwarf2_section_info *section = this_cu->section; + struct objfile *objfile = per_objfile.objfile; + struct dwarf2_section_info *section = this_cu.section; bfd *abfd = section->get_bfd_owner (); - gdb_assert (parent_cu != nullptr); - gdb_assert (dwo_file != nullptr); - if (dwarf_die_debug) gdb_printf (gdb_stdlog, "Reading %s unit at offset %s\n", - this_cu->is_debug_types ? "type" : "comp", - sect_offset_str (this_cu->sect_off)); + this_cu.is_debug_types ? "type" : "comp", + sect_offset_str (this_cu.sect_off)); - gdb_assert (per_objfile->get_cu (this_cu) == nullptr); + gdb_assert (per_objfile.get_cu (&this_cu) == nullptr); - dwarf2_section_info *abbrev_section = &dwo_file->sections.abbrev; + dwarf2_section_info *abbrev_section = &dwo_file.sections.abbrev; /* This is cheap if the section is already read in. */ section->read (objfile); - m_new_cu = std::make_unique<dwarf2_cu> (this_cu, per_objfile); + m_new_cu = std::make_unique<dwarf2_cu> (&this_cu, &per_objfile); - m_info_ptr = section->buffer + to_underlying (this_cu->sect_off); + m_info_ptr = section->buffer + to_underlying (this_cu.sect_off); const gdb_byte *begin_info_ptr = m_info_ptr; m_info_ptr - = read_and_check_comp_unit_head (per_objfile, &m_new_cu->header, section, + = read_and_check_comp_unit_head (&per_objfile, &m_new_cu->header, section, abbrev_section, m_info_ptr, - (this_cu->is_debug_types + (this_cu.is_debug_types ? rcuh_kind::TYPE : rcuh_kind::COMPILE)); - m_new_cu->str_offsets_base = parent_cu->str_offsets_base; - m_new_cu->addr_base = parent_cu->addr_base; + m_new_cu->str_offsets_base = parent_cu.str_offsets_base; + m_new_cu->addr_base = parent_cu.addr_base; - this_cu->set_length (m_new_cu->header.get_length_with_initial ()); + this_cu.set_length (m_new_cu->header.get_length_with_initial ()); /* Skip dummy compilation units. */ - if (m_info_ptr >= begin_info_ptr + this_cu->length () + if (m_info_ptr >= begin_info_ptr + this_cu.length () || peek_abbrev_code (abfd, m_info_ptr) == 0) m_dummy_p = true; else @@ -3233,7 +3227,7 @@ cutu_reader::cutu_reader (dwarf2_per_cu *this_cu, = abbrev_table::read (abbrev_section, m_new_cu->header.abbrev_sect_off); - this->init_cu_die_reader (m_new_cu.get (), section, dwo_file, + this->init_cu_die_reader (m_new_cu.get (), section, &dwo_file, m_abbrev_table_holder.get ()); m_top_level_die = this->read_toplevel_die (); } @@ -3295,12 +3289,12 @@ get_type_unit_group_key (struct dwarf2_cu *cu, const struct attribute *stmt_list static void process_psymtab_comp_unit (dwarf2_per_cu *this_cu, dwarf2_per_objfile *per_objfile, - cooked_index_storage *storage) + cooked_index_worker_result *storage) { cutu_reader *reader = storage->get_reader (this_cu); if (reader == nullptr) { - cutu_reader new_reader (this_cu, per_objfile, nullptr, nullptr, false, + cutu_reader new_reader (*this_cu, *per_objfile, nullptr, nullptr, false, language_minimal, &storage->get_abbrev_table_cache ()); @@ -3332,7 +3326,7 @@ process_psymtab_comp_unit (dwarf2_per_cu *this_cu, static void build_type_psymtabs_reader (cutu_reader *reader, - cooked_index_storage *storage) + cooked_index_worker_result *storage) { struct dwarf2_cu *cu = reader->cu (); dwarf2_per_cu *per_cu = cu->per_cu; @@ -3385,7 +3379,7 @@ struct tu_abbrev_offset static void build_type_psymtabs (dwarf2_per_objfile *per_objfile, - cooked_index_storage *storage) + cooked_index_worker_result *storage) { struct tu_stats *tu_stats = &per_objfile->per_bfd->tu_stats; abbrev_table_up abbrev_table; @@ -3447,7 +3441,7 @@ build_type_psymtabs (dwarf2_per_objfile *per_objfile, ++tu_stats->nr_uniq_abbrev_tables; } - cutu_reader reader (tu.sig_type, per_objfile, + cutu_reader reader (*tu.sig_type, *per_objfile, abbrev_table.get (), nullptr, false, language_minimal); if (!reader.is_dummy ()) @@ -3482,7 +3476,7 @@ print_tu_stats (dwarf2_per_objfile *per_objfile) static void process_skeletonless_type_unit (dwo_unit *dwo_unit, dwarf2_per_objfile *per_objfile, - cooked_index_storage *storage) + cooked_index_worker_result *storage) { dwarf2_per_bfd *per_bfd = per_objfile->per_bfd; @@ -3501,7 +3495,7 @@ process_skeletonless_type_unit (dwo_unit *dwo_unit, fill_in_sig_entry_from_dwo_entry (per_objfile, *sig_type_it, dwo_unit); /* This does the job that build_type_psymtabs would have done. */ - cutu_reader reader (*sig_type_it, per_objfile, nullptr, nullptr, false, + cutu_reader reader (**sig_type_it, *per_objfile, nullptr, nullptr, false, language_minimal); if (!reader.is_dummy ()) build_type_psymtabs_reader (&reader, storage); @@ -3513,7 +3507,7 @@ process_skeletonless_type_unit (dwo_unit *dwo_unit, static void process_skeletonless_type_units (dwarf2_per_objfile *per_objfile, - cooked_index_storage *storage) + cooked_index_worker_result *storage) { /* Skeletonless TUs in DWP files without .gdb_index is not supported yet. */ if (get_dwp_file (per_objfile) == nullptr) @@ -3559,7 +3553,7 @@ private: /* After the last DWARF-reading task has finished, this function does the remaining work to finish the scan. */ - void done_reading (); + void done_reading () override; /* An iterator for the comp units. */ using unit_iterator = std::vector<dwarf2_per_cu_up>::iterator; @@ -3573,12 +3567,13 @@ private: /* A storage object for "leftovers" -- see the 'start' method, but essentially things not parsed during the normal CU parsing passes. */ - cooked_index_storage m_index_storage; + cooked_index_worker_result m_index_storage; }; void -cooked_index_worker_debug_info::process_cus (size_t task_number, unit_iterator first, - unit_iterator end) +cooked_index_worker_debug_info::process_cus (size_t task_number, + unit_iterator first, + unit_iterator end) { SCOPE_EXIT { bfd_thread_cleanup (); }; @@ -3586,7 +3581,7 @@ cooked_index_worker_debug_info::process_cus (size_t task_number, unit_iterator f complaint_interceptor complaint_handler; std::vector<gdb_exception> errors; - cooked_index_storage thread_storage; + cooked_index_worker_result thread_storage; for (auto inner = first; inner != end; ++inner) { dwarf2_per_cu *per_cu = inner->get (); @@ -3597,43 +3592,24 @@ cooked_index_worker_debug_info::process_cus (size_t task_number, unit_iterator f } catch (gdb_exception &except) { - errors.push_back (std::move (except)); + thread_storage.note_error (std::move (except)); } } - m_results[task_number] = result_type (thread_storage.release (), - complaint_handler.release (), - std::move (errors), - thread_storage.release_parent_map ()); + thread_storage.done_reading (complaint_handler.release ()); + m_results[task_number] = std::move (thread_storage); } void cooked_index_worker_debug_info::done_reading () { - /* Only handle the scanning results here. Complaints and exceptions - can only be dealt with on the main thread. */ - std::vector<cooked_index_shard_up> shards; - - for (auto &one_result : m_results) - { - shards.push_back (std::move (std::get<0> (one_result))); - m_all_parents_map.add_map (std::get<3> (one_result)); - } - /* This has to wait until we read the CUs, we need the list of DWOs. */ process_skeletonless_type_units (m_per_objfile, &m_index_storage); - shards.push_back (m_index_storage.release ()); - shards.shrink_to_fit (); - - m_all_parents_map.add_map (m_index_storage.release_parent_map ()); + m_results.push_back (std::move (m_index_storage)); - dwarf2_per_bfd *per_bfd = m_per_objfile->per_bfd; - cooked_index *table - = (gdb::checked_static_cast<cooked_index *> - (per_bfd->index_table.get ())); - table->set_contents (std::move (shards), &m_warnings, - &m_all_parents_map); + /* Call into the base class. */ + cooked_index_worker::done_reading (); } void @@ -4223,7 +4199,7 @@ load_full_comp_unit (dwarf2_per_cu *this_cu, dwarf2_per_objfile *per_objfile, gdb_assert (! this_cu->is_debug_types); gdb_assert (per_objfile->get_cu (this_cu) == nullptr); - cutu_reader reader (this_cu, per_objfile, nullptr, nullptr, skip_partial, + cutu_reader reader (*this_cu, *per_objfile, nullptr, nullptr, skip_partial, pretend_language); if (reader.is_dummy ()) return; @@ -5456,12 +5432,11 @@ dwarf2_compute_name (const char *name, if (lang == language_cplus && strchr (name, '<') == NULL) { struct attribute *attr; - struct die_info *child; int first = 1; die->building_fullname = 1; - for (child = die->child; child != NULL; child = child->next) + for (die_info *child : die->children ()) { struct type *type; LONGEST value; @@ -5825,7 +5800,7 @@ read_import_statement (struct die_info *die, struct dwarf2_cu *cu) { struct objfile *objfile = cu->per_objfile->objfile; struct attribute *import_attr; - struct die_info *imported_die, *child_die; + struct die_info *imported_die; struct dwarf2_cu *imported_cu; const char *imported_name; const char *imported_name_prefix; @@ -5908,10 +5883,8 @@ read_import_statement (struct die_info *die, struct dwarf2_cu *cu) else canonical_name = imported_name; - if (die->tag == DW_TAG_imported_module - && cu->lang () == language_fortran) - for (child_die = die->child; child_die && child_die->tag; - child_die = child_die->next) + if (die->tag == DW_TAG_imported_module && cu->lang () == language_fortran) + for (die_info *child_die : die->children ()) { /* DWARF-4: A Fortran use statement with a “rename list” may be represented by an imported module entry with an import attribute @@ -6098,7 +6071,6 @@ read_file_scope (struct die_info *die, struct dwarf2_cu *cu) struct objfile *objfile = per_objfile->objfile; CORE_ADDR lowpc; struct attribute *attr; - struct die_info *child_die; unrelocated_addr unrel_low, unrel_high; get_scope_pc_bounds (die, &unrel_low, &unrel_high, cu); @@ -6145,15 +6117,9 @@ read_file_scope (struct die_info *die, struct dwarf2_cu *cu) handle_DW_AT_stmt_list (die, cu, fnd, unrel_low, unrel_low != unrel_high); /* Process all dies in compilation unit. */ - if (die->child != NULL) - { - child_die = die->child; - while (child_die && child_die->tag) - { - process_die (child_die, cu); - child_die = child_die->next; - } - } + for (die_info *child_die : die->children ()) + process_die (child_die, cu); + per_objfile->sym_cu = nullptr; /* Decode macro information, if present. Dwarf 2 macro information @@ -6309,22 +6275,13 @@ dwarf2_cu::setup_type_unit_groups (struct die_info *die) static void read_type_unit_scope (struct die_info *die, struct dwarf2_cu *cu) { - struct die_info *child_die; - /* Initialize (or reinitialize) the machinery for building symtabs. We do this before processing child DIEs, so that the line header table is available for DW_AT_decl_file. */ cu->setup_type_unit_groups (die); - if (die->child != NULL) - { - child_die = die->child; - while (child_die && child_die->tag) - { - process_die (child_die, cu); - child_die = child_die->next; - } - } + for (die_info *child_die : die->children ()) + process_die (child_die, cu); } /* DWO/DWP files. @@ -6377,8 +6334,8 @@ create_cus_hash_table (dwarf2_cu *cu, dwo_file &dwo_file) /* The length of the CU gets set by the cutu_reader just below. */ dwarf2_per_cu per_cu (per_bfd, §ion, sect_off, 0 /* length */, false /* is_dwz */); - cutu_reader reader (&per_cu, per_objfile, language_minimal, - cu, &dwo_file); + cutu_reader reader (per_cu, *per_objfile, language_minimal, + *cu, dwo_file); info_ptr += per_cu.length (); @@ -8224,9 +8181,7 @@ inherit_abstract_dies (struct die_info *die, struct dwarf2_cu *cu) std::vector<sect_offset> offsets; - for (die_info *child_die = die->child; - child_die && child_die->tag; - child_die = child_die->next) + for (die_info *child_die : die->children ()) { /* We are trying to process concrete instance entries: DW_TAG_call_site DIEs indeed have a DW_AT_abstract_origin tag, but @@ -8314,8 +8269,7 @@ inherit_abstract_dies (struct die_info *die, struct dwarf2_cu *cu) } auto offsets_it = offsets.begin (); - die_info *origin_child_die = origin_die->child; - while (origin_child_die != nullptr && origin_child_die->tag != 0) + for (die_info *origin_child_die : origin_die->children ()) { /* Is ORIGIN_CHILD_DIE referenced by any of the DIE children? */ while (offsets_it < offsets.end () @@ -8332,8 +8286,6 @@ inherit_abstract_dies (struct die_info *die, struct dwarf2_cu *cu) if (!origin_child_die->in_process) process_die (origin_child_die, origin_cu); } - - origin_child_die = origin_child_die->next; } origin_cu->list_in_scope = origin_previous_list_in_scope; @@ -8424,7 +8376,6 @@ read_func_scope (struct die_info *die, struct dwarf2_cu *cu) struct context_stack *newobj; CORE_ADDR lowpc; CORE_ADDR highpc; - struct die_info *child_die; struct attribute *attr, *call_line, *call_file; const char *name; struct block *block; @@ -8501,7 +8452,7 @@ read_func_scope (struct die_info *die, struct dwarf2_cu *cu) /* If we have any template arguments, then we must allocate a different sort of symbol. */ - for (child_die = die->child; child_die; child_die = child_die->next) + for (die_info *child_die : die->children ()) { if (child_die->tag == DW_TAG_template_type_param || child_die->tag == DW_TAG_template_value_param) @@ -8539,23 +8490,18 @@ read_func_scope (struct die_info *die, struct dwarf2_cu *cu) cu->list_in_scope = cu->get_builder ()->get_local_symbols (); - if (die->child != NULL) + for (die_info *child_die : die->children ()) { - child_die = die->child; - while (child_die && child_die->tag) + if (child_die->tag == DW_TAG_template_type_param + || child_die->tag == DW_TAG_template_value_param) { - if (child_die->tag == DW_TAG_template_type_param - || child_die->tag == DW_TAG_template_value_param) - { - struct symbol *arg = new_symbol (child_die, NULL, cu); + struct symbol *arg = new_symbol (child_die, NULL, cu); - if (arg != NULL) - template_args.push_back (arg); - } - else - process_die (child_die, cu); - child_die = child_die->next; + if (arg != NULL) + template_args.push_back (arg); } + else + process_die (child_die, cu); } inherit_abstract_dies (die, cu); @@ -8571,13 +8517,9 @@ read_func_scope (struct die_info *die, struct dwarf2_cu *cu) while (spec_die) { - child_die = spec_die->child; - while (child_die && child_die->tag) - { - if (child_die->tag == DW_TAG_imported_module) - process_die (child_die, spec_cu); - child_die = child_die->next; - } + for (die_info *child_die : spec_die->children ()) + if (child_die->tag == DW_TAG_imported_module) + process_die (child_die, spec_cu); /* In some cases, GCC generates specification DIEs that themselves contain DW_AT_specification attributes. */ @@ -8646,7 +8588,6 @@ read_lexical_block_scope (struct die_info *die, struct dwarf2_cu *cu) { dwarf2_per_objfile *per_objfile = cu->per_objfile; CORE_ADDR lowpc, highpc; - struct die_info *child_die; /* Ignore blocks with missing or invalid low and high pc attributes. */ /* ??? Perhaps consider discontiguous blocks defined by DW_AT_ranges @@ -8661,9 +8602,7 @@ read_lexical_block_scope (struct die_info *die, struct dwarf2_cu *cu) /* DW_TAG_lexical_block has no attributes, process its children as if there was no wrapping by that DW_TAG_lexical_block. GCC does no longer produces such DWARF since GCC r224161. */ - for (child_die = die->child; - child_die != NULL && child_die->tag; - child_die = child_die->next) + for (die_info *child_die : die->children ()) { /* We might already be processing this DIE. This can happen in an unusual circumstance -- where a subroutine A @@ -8682,15 +8621,9 @@ read_lexical_block_scope (struct die_info *die, struct dwarf2_cu *cu) highpc = per_objfile->relocate (unrel_high); cu->get_builder ()->push_context (0, lowpc); - if (die->child != NULL) - { - child_die = die->child; - while (child_die && child_die->tag) - { - process_die (child_die, cu); - child_die = child_die->next; - } - } + for (die_info *child_die : die->children ()) + process_die (child_die, cu); + inherit_abstract_dies (die, cu); struct context_stack cstk = cu->get_builder ()->pop_context (); @@ -8733,7 +8666,6 @@ read_call_site_scope (struct die_info *die, struct dwarf2_cu *cu) struct gdbarch *gdbarch = objfile->arch (); struct attribute *attr; int nparams; - struct die_info *child_die; attr = dwarf2_attr (die, DW_AT_call_return_pc, cu); if (attr == NULL) @@ -8754,8 +8686,7 @@ read_call_site_scope (struct die_info *die, struct dwarf2_cu *cu) /* Count parameters at the caller. */ nparams = 0; - for (child_die = die->child; child_die && child_die->tag; - child_die = child_die->next) + for (die_info *child_die : die->children ()) { if (child_die->tag != DW_TAG_call_site_parameter && child_die->tag != DW_TAG_GNU_call_site_parameter) @@ -8924,9 +8855,7 @@ read_call_site_scope (struct die_info *die, struct dwarf2_cu *cu) "block nor reference, for DIE %s [in module %s]"), sect_offset_str (die->sect_off), objfile_name (objfile)); - for (child_die = die->child; - child_die && child_die->tag; - child_die = child_die->next) + for (die_info *child_die : die->children ()) { struct call_site_parameter *parameter; struct attribute *loc, *origin; @@ -9646,7 +9575,6 @@ dwarf2_get_subprogram_pc_bounds (struct die_info *die, struct dwarf2_cu *cu) { unrelocated_addr low, high; - struct die_info *child = die->child; if (dwarf2_get_pc_bounds (die, &low, &high, cu, nullptr, nullptr) >= PC_BOUNDS_RANGES) @@ -9664,12 +9592,11 @@ dwarf2_get_subprogram_pc_bounds (struct die_info *die, subprograms, then check their pc bounds. Likewise, we need to check lexical blocks as well, as they may also contain subprogram definitions. */ - while (child && child->tag) + for (die_info *child : die->children ()) { if (child->tag == DW_TAG_subprogram || child->tag == DW_TAG_lexical_block) dwarf2_get_subprogram_pc_bounds (child, lowpc, highpc, cu); - child = child->next; } } @@ -9695,9 +9622,7 @@ get_scope_pc_bounds (struct die_info *die, } else { - struct die_info *child = die->child; - - while (child && child->tag) + for (die_info *child : die->children ()) { switch (child->tag) { case DW_TAG_subprogram: @@ -9725,8 +9650,6 @@ get_scope_pc_bounds (struct die_info *die, /* Ignore. */ break; } - - child = child->next; } } @@ -11277,9 +11200,7 @@ handle_variant_part (struct die_info *die, struct type *type, objfile_name (cu->per_objfile->objfile)); } - for (die_info *child_die = die->child; - child_die != NULL; - child_die = child_die->next) + for (die_info *child_die : die->children ()) handle_struct_member_die (child_die, type, fi, template_args, cu); } @@ -11330,9 +11251,7 @@ handle_variant (struct die_info *die, struct type *type, else variant.discriminant_value = discr->constant_value (0); - for (die_info *variant_child = die->child; - variant_child != NULL; - variant_child = variant_child->next) + for (die_info *variant_child : die->children ()) handle_struct_member_die (variant_child, type, fi, template_args, cu); variant.last_field = fi->fields.size (); @@ -11401,7 +11320,6 @@ static void process_structure_scope (struct die_info *die, struct dwarf2_cu *cu) { struct objfile *objfile = cu->per_objfile->objfile; - struct die_info *child_die; struct type *type; type = get_die_type (die, cu); @@ -11414,13 +11332,8 @@ process_structure_scope (struct die_info *die, struct dwarf2_cu *cu) struct field_info fi; std::vector<struct symbol *> template_args; - child_die = die->child; - - while (child_die && child_die->tag) - { - handle_struct_member_die (child_die, type, &fi, &template_args, cu); - child_die = child_die->next; - } + for (die_info *child_die : die->children ()) + handle_struct_member_die (child_die, type, &fi, &template_args, cu); /* Attach template arguments to type. */ if (!template_args.empty ()) @@ -11558,9 +11471,7 @@ process_structure_scope (struct die_info *die, struct dwarf2_cu *cu) current die is a declaration. Normally, of course, a declaration won't have any children at all. */ - child_die = die->child; - - while (child_die != NULL && child_die->tag) + for (die_info *child_die : die->children ()) { if (child_die->tag == DW_TAG_member || child_die->tag == DW_TAG_variable @@ -11572,8 +11483,6 @@ process_structure_scope (struct die_info *die, struct dwarf2_cu *cu) } else process_die (child_die, cu); - - child_die = child_die->next; } /* Do not consider external references. According to the DWARF standard, @@ -11673,16 +11582,13 @@ update_enumeration_type_from_children (struct die_info *die, struct type *type, struct dwarf2_cu *cu) { - struct die_info *child_die; int unsigned_enum = 1; int flag_enum = 1; auto_obstack obstack; std::vector<struct field> fields; - for (child_die = die->child; - child_die != NULL && child_die->tag; - child_die = child_die->next) + for (die_info *child_die : die->children ()) { struct attribute *attr; LONGEST value; @@ -11840,10 +11746,7 @@ process_enumeration_scope (struct die_info *die, struct dwarf2_cu *cu) if (die->child != NULL) { - struct die_info *child_die; - - child_die = die->child; - while (child_die && child_die->tag) + for (die_info *child_die : die->children ()) { if (child_die->tag != DW_TAG_enumerator) { @@ -11851,8 +11754,6 @@ process_enumeration_scope (struct die_info *die, struct dwarf2_cu *cu) } else new_symbol (child_die, this_type, cu); - - child_die = child_die->next; } } @@ -12033,9 +11934,7 @@ quirk_ada_thick_pointer (struct die_info *die, struct dwarf2_cu *cu, int bounds_offset = -1; int max_align = -1; std::vector<struct field> range_fields; - for (struct die_info *child_die = die->child; - child_die; - child_die = child_die->next) + for (die_info *child_die : die->children ()) { if (child_die->tag == DW_TAG_subrange_type) { @@ -12136,7 +12035,6 @@ static struct type * read_array_type (struct die_info *die, struct dwarf2_cu *cu) { struct objfile *objfile = cu->per_objfile->objfile; - struct die_info *child_die; struct type *type; struct type *element_type, *range_type, *index_type; struct attribute *attr; @@ -12191,8 +12089,7 @@ read_array_type (struct die_info *die, struct dwarf2_cu *cu) } std::vector<struct type *> range_types; - child_die = die->child; - while (child_die && child_die->tag) + for (die_info *child_die : die->children ()) { if (child_die->tag == DW_TAG_subrange_type || child_die->tag == DW_TAG_generic_subrange) @@ -12206,7 +12103,6 @@ read_array_type (struct die_info *die, struct dwarf2_cu *cu) range_types.push_back (child_type); } } - child_die = child_die->next; } if (range_types.empty ()) @@ -12473,15 +12369,12 @@ read_common_block (struct die_info *die, struct dwarf2_cu *cu) if (die->child != NULL) { struct objfile *objfile = cu->per_objfile->objfile; - struct die_info *child_die; - size_t n_entries = 0, size; + size_t size; struct common_block *common_block; struct symbol *sym; - for (child_die = die->child; - child_die && child_die->tag; - child_die = child_die->next) - ++n_entries; + auto range = die->children (); + size_t n_entries = std::distance (range.begin (), range.end ()); size = (sizeof (struct common_block) + (n_entries - 1) * sizeof (struct symbol *)); @@ -12491,9 +12384,7 @@ read_common_block (struct die_info *die, struct dwarf2_cu *cu) memset (common_block->contents, 0, n_entries * sizeof (struct symbol *)); common_block->n_entries = 0; - for (child_die = die->child; - child_die && child_die->tag; - child_die = child_die->next) + for (die_info *child_die : die->children ()) { /* Create the symbol in the DW_TAG_common_block block in the current symbol scope. */ @@ -12616,13 +12507,8 @@ read_namespace (struct die_info *die, struct dwarf2_cu *cu) if (die->child != NULL) { - struct die_info *child_die = die->child; - - while (child_die && child_die->tag) - { - process_die (child_die, cu); - child_die = child_die->next; - } + for (die_info *child_die : die->children ()) + process_die (child_die, cu); } } @@ -12660,17 +12546,13 @@ read_module_type (struct die_info *die, struct dwarf2_cu *cu) static void read_module (struct die_info *die, struct dwarf2_cu *cu) { - struct die_info *child_die = die->child; struct type *type; type = read_type_die (die, cu); new_symbol (die, type, cu); - while (child_die && child_die->tag) - { - process_die (child_die, cu); - child_die = child_die->next; - } + for (die_info *child_die : die->children ()) + process_die (child_die, cu); } /* Return the name of the namespace represented by DIE. Set @@ -13168,22 +13050,18 @@ read_subroutine_type (struct die_info *die, struct dwarf2_cu *cu) if (die->child != NULL) { struct type *void_type = builtin_type (objfile)->builtin_void; - struct die_info *child_die; int nparams, iparams; /* Count the number of parameters. FIXME: GDB currently ignores vararg functions, but knows about vararg member functions. */ nparams = 0; - child_die = die->child; - while (child_die && child_die->tag) + for (die_info *child_die : die->children ()) { if (child_die->tag == DW_TAG_formal_parameter) nparams++; else if (child_die->tag == DW_TAG_unspecified_parameters) ftype->set_has_varargs (true); - - child_die = child_die->next; } /* Allocate storage for parameters and fill them in. */ @@ -13195,8 +13073,7 @@ read_subroutine_type (struct die_info *die, struct dwarf2_cu *cu) ftype->field (iparams).set_type (void_type); iparams = 0; - child_die = die->child; - while (child_die && child_die->tag) + for (die_info *child_die : die->children ()) { if (child_die->tag == DW_TAG_formal_parameter) { @@ -13253,7 +13130,6 @@ read_subroutine_type (struct die_info *die, struct dwarf2_cu *cu) ftype->field (iparams).set_type (arg_type); iparams++; } - child_die = child_die->next; } } @@ -15423,7 +15299,7 @@ dwarf2_read_addr_index (dwarf2_per_cu *per_cu, dwarf2_per_objfile *per_objfile, } else { - cutu_reader reader (per_cu, per_objfile, nullptr, nullptr, false, + cutu_reader reader (*per_cu, *per_objfile, nullptr, nullptr, false, language_minimal); addr_base = reader.cu ()->addr_base; addr_size = reader.cu ()->header.addr_size; @@ -17767,7 +17643,6 @@ guess_full_die_structure_name (struct die_info *die, struct dwarf2_cu *cu) { struct die_info *spec_die; struct dwarf2_cu *spec_cu; - struct die_info *child; struct objfile *objfile = cu->per_objfile->objfile; spec_cu = cu; @@ -17778,9 +17653,7 @@ guess_full_die_structure_name (struct die_info *die, struct dwarf2_cu *cu) cu = spec_cu; } - for (child = die->child; - child != NULL; - child = child->next) + for (die_info *child : die->children ()) { if (child->tag == DW_TAG_subprogram) { @@ -18106,18 +17979,19 @@ unnamed_template_tag_name (die_info *die, dwarf2_cu *cu) arrive at our entry. */ size_t nth_unnamed = 0; - die_info *child = die->parent->child; - while (child != die) - { - gdb_assert (child != nullptr); - if (child->tag == DW_TAG_template_type_param - || child->tag == DW_TAG_template_value_param) - { - if (dwarf2_attr (child, DW_AT_name, cu) == nullptr) - ++nth_unnamed; - } - child = child->next; - } + for (die_info *child : die->parent->children ()) + { + if (child == die) + break; + + gdb_assert (child != nullptr); + if (child->tag == DW_TAG_template_type_param + || child->tag == DW_TAG_template_value_param) + { + if (dwarf2_attr (child, DW_AT_name, cu) == nullptr) + ++nth_unnamed; + } + } const std::string name_str = "<unnamed" + std::to_string (nth_unnamed) + ">"; return cu->per_objfile->objfile->intern (name_str.c_str ()); @@ -18925,7 +18799,7 @@ read_signatured_type (signatured_type *sig_type, gdb_assert (sig_type->is_debug_types); gdb_assert (per_objfile->get_cu (sig_type) == nullptr); - cutu_reader reader (sig_type, per_objfile, nullptr, nullptr, false, + cutu_reader reader (*sig_type, *per_objfile, nullptr, nullptr, false, language_minimal); if (!reader.is_dummy ()) @@ -19438,7 +19312,7 @@ dwarf2_per_cu::ensure_lang (dwarf2_per_objfile *per_objfile) /* Constructing this object will set the language as a side effect. */ - cutu_reader reader (this, per_objfile, nullptr, per_objfile->get_cu (this), + cutu_reader reader (*this, *per_objfile, nullptr, per_objfile->get_cu (this), true, language_minimal, nullptr); } diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h index ba2dd07..7f56dac 100644 --- a/gdb/dwarf2/read.h +++ b/gdb/dwarf2/read.h @@ -921,19 +921,19 @@ class cutu_reader { public: - cutu_reader (dwarf2_per_cu *this_cu, - dwarf2_per_objfile *per_objfile, + cutu_reader (dwarf2_per_cu &this_cu, + dwarf2_per_objfile &per_objfile, const struct abbrev_table *abbrev_table, dwarf2_cu *existing_cu, bool skip_partial, enum language pretend_language, const abbrev_table_cache *cache = nullptr); - cutu_reader (dwarf2_per_cu *this_cu, - dwarf2_per_objfile *per_objfile, + cutu_reader (dwarf2_per_cu &this_cu, + dwarf2_per_objfile &per_objfile, enum language pretend_language, - struct dwarf2_cu *parent_cu, - struct dwo_file *dwo_file); + struct dwarf2_cu &parent_cu, + struct dwo_file &dwo_file); DISABLE_COPY_AND_ASSIGN (cutu_reader); @@ -1003,8 +1003,7 @@ private: void read_cutu_die_from_dwo (dwarf2_cu *cu, dwo_unit *dwo_unit, die_info *stub_comp_unit_die, - const char *stub_comp_dir, - abbrev_table_up *result_dwo_abbrev_table); + const char *stub_comp_dir); void prepare_one_comp_unit (struct dwarf2_cu *cu, enum language pretend_language); diff --git a/gdb/elfread.c b/gdb/elfread.c index 5be3118..3756fa3 100644 --- a/gdb/elfread.c +++ b/gdb/elfread.c @@ -1062,8 +1062,8 @@ elf_read_minimal_symbols (struct objfile *objfile, int symfile_flags, const struct elfinfo *ei) { bfd *synth_abfd, *abfd = objfile->obfd.get (); - long symcount = 0, dynsymcount = 0, synthcount, storage_needed; - asymbol **symbol_table = NULL, **dyn_symbol_table = NULL; + long dynsymcount = 0, synthcount; + asymbol **dyn_symbol_table = NULL; asymbol *synthsyms; symtab_create_debug_printf ("reading minimal symbols of objfile %s", @@ -1087,32 +1087,16 @@ elf_read_minimal_symbols (struct objfile *objfile, int symfile_flags, /* Process the normal ELF symbol table first. */ - storage_needed = bfd_get_symtab_upper_bound (objfile->obfd.get ()); - if (storage_needed < 0) - error (_("Can't read symbols from %s: %s"), - bfd_get_filename (objfile->obfd.get ()), - bfd_errmsg (bfd_get_error ())); + gdb::array_view<asymbol *> symbol_table + = gdb_bfd_canonicalize_symtab (objfile->obfd.get ()); - if (storage_needed > 0) - { - /* Memory gets permanently referenced from ABFD after - bfd_canonicalize_symtab so it must not get freed before ABFD gets. */ - - symbol_table = (asymbol **) bfd_alloc (abfd, storage_needed); - symcount = bfd_canonicalize_symtab (objfile->obfd.get (), symbol_table); - - if (symcount < 0) - error (_("Can't read symbols from %s: %s"), - bfd_get_filename (objfile->obfd.get ()), - bfd_errmsg (bfd_get_error ())); - - elf_symtab_read (reader, objfile, ST_REGULAR, symcount, symbol_table, - false); - } + elf_symtab_read (reader, objfile, ST_REGULAR, symbol_table.size (), + symbol_table.data (), false); /* Add the dynamic symbols. */ - storage_needed = bfd_get_dynamic_symtab_upper_bound (objfile->obfd.get ()); + long storage_needed + = bfd_get_dynamic_symtab_upper_bound (objfile->obfd.get ()); if (storage_needed > 0) { @@ -1157,7 +1141,8 @@ elf_read_minimal_symbols (struct objfile *objfile, int symfile_flags, /* Add synthetic symbols - for instance, names for any PLT entries. */ - synthcount = bfd_get_synthetic_symtab (synth_abfd, symcount, symbol_table, + synthcount = bfd_get_synthetic_symtab (synth_abfd, symbol_table.size (), + symbol_table.data (), dynsymcount, dyn_symbol_table, &synthsyms); if (synthcount > 0) diff --git a/gdb/frame.c b/gdb/frame.c index 2fb06a0..88560b8 100644 --- a/gdb/frame.c +++ b/gdb/frame.c @@ -2325,7 +2325,22 @@ get_prev_frame_always_1 (const frame_info_ptr &this_frame) until we have unwound all the way down to the previous non-inline frame. */ if (get_frame_type (this_frame) == INLINE_FRAME) - return get_prev_frame_maybe_check_cycle (this_frame); + { + frame_info_ptr fi = get_prev_frame_maybe_check_cycle (this_frame); + + /* If this_frame is the current frame, then compute and stash its frame + id so that the cycle check in get_prev_frame_maybe_check_cycle works + correctly in the case where inline frame 0 has been duplicated. + + The this_id.p check is required to avoid recursion as computing the + frame id results in a call to inline_frame_this_id which calls back + into get_prev_frame_always. */ + if (this_frame->level == 0 + && this_frame->this_id.p != frame_id_status::COMPUTING) + get_frame_id (this_frame); + + return fi; + } /* If this_frame is the current frame, then compute and stash its frame id prior to fetching and computing the frame id of the diff --git a/gdb/gdb_bfd.c b/gdb/gdb_bfd.c index 8380c53..1a57b3c 100644 --- a/gdb/gdb_bfd.c +++ b/gdb/gdb_bfd.c @@ -143,6 +143,13 @@ struct gdb_bfd_data /* Table of all the bfds this bfd has included. */ std::vector<gdb_bfd_ref_ptr> included_bfds; + /* This is used by gdb_bfd_canonicalize_symtab to hold the symbols + returned by canonicalization. */ + std::optional<gdb::def_vector<asymbol *>> symbol_table; + /* If an error occurred while canonicalizing the symtab, this holds + the error message. */ + std::string symbol_error; + /* The registry. */ registry<bfd> registry_fields; @@ -1177,6 +1184,54 @@ gdb_bfd_errmsg (bfd_error_type error_tag, char **matching) return ret; } +/* See gdb_bfd.h. */ + +gdb::array_view<asymbol *> +gdb_bfd_canonicalize_symtab (bfd *abfd, bool should_throw) +{ + struct gdb_bfd_data *gdata = (struct gdb_bfd_data *) bfd_usrdata (abfd); + + if (!gdata->symbol_table.has_value ()) + { + /* Ensure it exists. */ + gdb::def_vector<asymbol *> &symbol_table + = gdata->symbol_table.emplace (); + + long storage_needed = bfd_get_symtab_upper_bound (abfd); + if (storage_needed < 0) + gdata->symbol_error = bfd_errmsg (bfd_get_error ()); + else if (storage_needed > 0) + { + symbol_table.resize (storage_needed / sizeof (asymbol *)); + long number_of_symbols + = bfd_canonicalize_symtab (abfd, symbol_table.data ()); + if (number_of_symbols < 0) + { + symbol_table.clear (); + gdata->symbol_error = bfd_errmsg (bfd_get_error ()); + } + } + } + + if (!gdata->symbol_error.empty ()) + { + if (should_throw) + error (_("Cannot parse symbols of \"%s\": %s"), + bfd_get_filename (abfd), gdata->symbol_error.c_str ()); + return {}; + } + + gdb::def_vector<asymbol *> &symbol_table = *gdata->symbol_table; + if (symbol_table.empty ()) + return {}; + + /* bfd_canonicalize_symtab adds a trailing NULL, but don't include + this in the array view. */ + gdb_assert (symbol_table.back () == nullptr); + return gdb::make_array_view (symbol_table.data (), + symbol_table.size () - 1); +} + /* Implement the 'maint info bfd' command. */ static void diff --git a/gdb/gdb_bfd.h b/gdb/gdb_bfd.h index d35f2d6..7830bf3 100644 --- a/gdb/gdb_bfd.h +++ b/gdb/gdb_bfd.h @@ -274,4 +274,16 @@ extern std::string gdb_bfd_errmsg (bfd_error_type error_tag, char **matching); extern void gdb_bfd_init (); +/* A wrapper for bfd_canonicalize_symtab that caches the result. This + is important to avoid excess memory use on repeated calls. See + PR gdb/32758. bfd_canonicalize_symtab should not be called directly + by other code in gdb. + + When SHOULD_THROW is true (the default), this will throw an + exception if symbols could not be read. When SHOULD_THROW is + false, an empty view is returned instead. */ + +extern gdb::array_view<asymbol *> gdb_bfd_canonicalize_symtab + (bfd *abfd, bool should_throw = true); + #endif /* GDB_GDB_BFD_H */ diff --git a/gdb/gdbarch-selftests.c b/gdb/gdbarch-selftests.c index 27b17d17..8f42557 100644 --- a/gdb/gdbarch-selftests.c +++ b/gdb/gdbarch-selftests.c @@ -127,6 +127,9 @@ register_to_value_test (struct gdbarch *gdbarch) static void register_name_test (struct gdbarch *gdbarch) { + if (selftest_skip_warning_arch (gdbarch)) + return; + scoped_mock_context<test_target_ops> mockctx (gdbarch); /* Track the number of times each register name appears. */ diff --git a/gdb/go32-nat.c b/gdb/go32-nat.c index 8453366..7852f56 100644 --- a/gdb/go32-nat.c +++ b/gdb/go32-nat.c @@ -697,7 +697,7 @@ go32_nat_target::create_inferior (const char *exec_file, "not enough memory.\n")); /* Parse the command line and create redirections. */ - if (strpbrk (args, "<>")) + if (strpbrk (args, "<>") != nullptr) { if (redir_cmdline_parse (args, &child_cmd) == 0) args = child_cmd.command; diff --git a/gdb/gstack-1.in b/gdb/gstack-1.in index 5e41329..25339d9 100755 --- a/gdb/gstack-1.in +++ b/gdb/gstack-1.in @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (C) 2024 Free Software Foundation, Inc. +# Copyright (C) 2024-2025 Free Software Foundation, Inc. # 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 @@ -22,8 +22,8 @@ GDB=${GDB:-$(command -v gdb)} GDBARGS=${GDBARGS:-} AWK=${AWK:-} -PKGVERSION=@PKGVERSION@ -VERSION=@VERSION@ +PKGVERSION="@PKGVERSION@" +VERSION="@VERSION@" # Find an appropriate awk interpreter if one was not specified # via the environment. @@ -132,7 +132,7 @@ EOF ) # Run GDB and remove some unwanted noise. -"$GDB" --quiet -nx --readnever $GDBARGS <<EOF | +"$GDB" --quiet -nx $GDBARGS <<EOF | set width 0 set height 0 set pagination no diff --git a/gdb/i386-linux-tdep.c b/gdb/i386-linux-tdep.c index 3fb1c17..2b7bd2b 100644 --- a/gdb/i386-linux-tdep.c +++ b/gdb/i386-linux-tdep.c @@ -484,15 +484,15 @@ i386_canonicalize_syscall (int syscall) SYSCALL_MAP (settimeofday); SYSCALL_MAP_RENAME (getgroups, gdb_sys_getgroups16); SYSCALL_MAP_RENAME (setgroups, gdb_sys_setgroups16); - SYSCALL_MAP_RENAME (select, gdb_old_select); + SYSCALL_MAP_RENAME (select, gdb_sys_old_select); SYSCALL_MAP (symlink); SYSCALL_MAP_RENAME (oldlstat, gdb_sys_lstat); SYSCALL_MAP (readlink); SYSCALL_MAP (uselib); SYSCALL_MAP (swapon); SYSCALL_MAP (reboot); - SYSCALL_MAP_RENAME (readdir, gdb_old_readdir); - SYSCALL_MAP_RENAME (mmap, gdb_old_mmap); + SYSCALL_MAP_RENAME (readdir, gdb_sys_old_readdir); + SYSCALL_MAP_RENAME (mmap, gdb_sys_old_mmap); SYSCALL_MAP (munmap); SYSCALL_MAP (truncate); SYSCALL_MAP (ftruncate); diff --git a/gdb/language.c b/gdb/language.c index a8548a2..4208c23 100644 --- a/gdb/language.c +++ b/gdb/language.c @@ -677,14 +677,6 @@ language_defn::is_string_type_p (struct type *type) const return c_is_string_type_p (type); } -/* See language.h. */ - -std::unique_ptr<compile_instance> -language_defn::get_compile_instance () const -{ - return {}; -} - /* The default implementation of the get_symbol_name_matcher_inner method from the language_defn class. Matches with strncmp_iw. */ diff --git a/gdb/language.h b/gdb/language.h index e6bfa3c..5e9599d 100644 --- a/gdb/language.h +++ b/gdb/language.h @@ -36,7 +36,6 @@ struct value_print_options; struct type_print_options; struct lang_varobj_ops; struct parser_state; -class compile_instance; struct completion_match_for_lcd; class innermost_block_tracker; @@ -390,37 +389,6 @@ struct language_defn symbol_name_matcher_ftype *get_symbol_name_matcher (const lookup_name_info &lookup_name) const; - /* If this language allows compilation from the gdb command line, - then this method will return an instance of struct gcc_context - appropriate to the language. If compilation for this language is - generally supported, but something goes wrong then an exception - is thrown. If compilation is not supported for this language - then this method returns NULL. */ - - virtual std::unique_ptr<compile_instance> get_compile_instance () const; - - /* This method must be overridden if 'get_compile_instance' is - overridden. - - This takes the user-supplied text and returns a new bit of code - to compile. - - INST is the compiler instance being used. - INPUT is the user's input text. - GDBARCH is the architecture to use. - EXPR_BLOCK is the block in which the expression is being - parsed. - EXPR_PC is the PC at which the expression is being parsed. */ - - virtual std::string compute_program (compile_instance *inst, - const char *input, - struct gdbarch *gdbarch, - const struct block *expr_block, - CORE_ADDR expr_pc) const - { - gdb_assert_not_reached ("language_defn::compute_program"); - } - /* Hash the given symbol search name. */ virtual unsigned int search_name_hash (const char *name) const; diff --git a/gdb/linux-record.c b/gdb/linux-record.c index 0b2709b..2f66337 100644 --- a/gdb/linux-record.c +++ b/gdb/linux-record.c @@ -609,7 +609,7 @@ record_linux_system_call (enum gdb_syscall syscall, case gdb_sys_setgroups16: break; - case gdb_old_select: + case gdb_sys_old_select: { unsigned long sz_sel_arg = tdep->size_long + tdep->size_pointer * 4; gdb_byte *a = (gdb_byte *) alloca (sz_sel_arg); @@ -668,12 +668,12 @@ record_linux_system_call (enum gdb_syscall syscall, return 1; break; - case gdb_old_readdir: + case gdb_sys_old_readdir: if (record_mem_at_reg (regcache, tdep->arg2, tdep->size_old_dirent)) return -1; break; - case gdb_old_mmap: + case gdb_sys_old_mmap: break; case gdb_sys_munmap: diff --git a/gdb/linux-record.h b/gdb/linux-record.h index 6d6ba07..6656106 100644 --- a/gdb/linux-record.h +++ b/gdb/linux-record.h @@ -266,15 +266,15 @@ enum gdb_syscall { gdb_sys_settimeofday = 79, gdb_sys_getgroups16 = 80, gdb_sys_setgroups16 = 81, - gdb_old_select = 82, + gdb_sys_old_select = 82, gdb_sys_symlink = 83, gdb_sys_lstat = 84, gdb_sys_readlink = 85, gdb_sys_uselib = 86, gdb_sys_swapon = 87, gdb_sys_reboot = 88, - gdb_old_readdir = 89, - gdb_old_mmap = 90, + gdb_sys_old_readdir = 89, + gdb_sys_old_mmap = 90, gdb_sys_munmap = 91, gdb_sys_truncate = 92, gdb_sys_ftruncate = 93, diff --git a/gdb/loongarch-linux-tdep.c b/gdb/loongarch-linux-tdep.c index bd42d09..031031b 100644 --- a/gdb/loongarch-linux-tdep.c +++ b/gdb/loongarch-linux-tdep.c @@ -812,7 +812,7 @@ loongarch_canonicalize_syscall (enum loongarch_syscall syscall_number) SYSCALL_MAP (clone); SYSCALL_MAP (execve); - SYSCALL_MAP_RENAME (mmap, gdb_sys_mmap2); + SYSCALL_MAP_RENAME (mmap, gdb_sys_old_mmap); SYSCALL_MAP (fadvise64); SYSCALL_MAP (swapon); diff --git a/gdb/maint-test-options.c b/gdb/maint-test-options.c index 9d76817..da9765e 100644 --- a/gdb/maint-test-options.c +++ b/gdb/maint-test-options.c @@ -137,6 +137,7 @@ struct test_options_opts int pint_unl_opt = 0; std::string string_opt; std::string filename_opt; + ui_file_style::color color_opt { ui_file_style::MAGENTA }; test_options_opts () = default; @@ -149,7 +150,7 @@ struct test_options_opts gdb_printf (file, _("-flag %d -xx1 %d -xx2 %d -bool %d " "-enum %s -uint-unl %s -pint-unl %s -string '%s' " - "-filename '%s' -- %s\n"), + "-filename '%s' -color %s -- %s\n"), flag_opt, xx1_opt, xx2_opt, @@ -163,6 +164,7 @@ struct test_options_opts : plongest (pint_unl_opt)), string_opt.c_str (), filename_opt.c_str (), + color_opt.to_string ().c_str (), args); } }; @@ -245,6 +247,14 @@ static const gdb::option::option_def test_options_option_defs[] = { nullptr, /* show_cmd_cb */ N_("A filename option."), }, + + /* A color option. */ + gdb::option::color_option_def<test_options_opts> { + "color", + [] (test_options_opts *opts) { return &opts->color_opt; }, + nullptr, /* show_cmd_cb */ + N_("A color option."), + }, }; /* Create an option_def_group for the test_options_opts options, with diff --git a/gdb/observable.h b/gdb/observable.h index deea1ff..c50891e 100644 --- a/gdb/observable.h +++ b/gdb/observable.h @@ -102,10 +102,14 @@ extern observable<inferior */* parent_inf */, inferior */* child_inf */, extern observable<solib &/* solib */> solib_loaded; /* The shared library SOLIB has been unloaded from program space PSPACE. + The SILENT argument indicates that GDB doesn't wish to notify the CLI + about any non-error consequences of unloading the solib, e.g. when + breakpoints are disabled. + Note when gdb calls this observer, the library's symbols have not been unloaded yet, and thus are still available. */ extern observable<program_space *, const solib &/* solib */, - bool /* still_in_use */> solib_unloaded; + bool /* still_in_use */, bool /* silent */> solib_unloaded; /* The symbol file specified by OBJFILE has been loaded. */ extern observable<struct objfile */* objfile */> new_objfile; diff --git a/gdb/python/lib/gdb/dap/__init__.py b/gdb/python/lib/gdb/dap/__init__.py index 145aeb6..08ca33f 100644 --- a/gdb/python/lib/gdb/dap/__init__.py +++ b/gdb/python/lib/gdb/dap/__init__.py @@ -26,6 +26,7 @@ from . import startup # server object. "F401" is the flake8 "imported but unused" code. from . import breakpoint # noqa: F401 from . import bt # noqa: F401 +from . import completions # noqa: F401 from . import disassemble # noqa: F401 from . import evaluate # noqa: F401 from . import launch # noqa: F401 diff --git a/gdb/python/lib/gdb/dap/completions.py b/gdb/python/lib/gdb/dap/completions.py new file mode 100644 index 0000000..85acc43 --- /dev/null +++ b/gdb/python/lib/gdb/dap/completions.py @@ -0,0 +1,60 @@ +# Copyright 2025 Free Software Foundation, Inc. + +# 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/>. + +from typing import Optional + +from .frames import select_frame +from .server import capability, import_column, import_line, request +from .startup import exec_mi_and_log + + +@request("completions") +@capability("supportsCompletionsRequest") +@capability("completionTriggerCharacters", [" ", "."]) +def completions( + *, + frameId: Optional[int] = None, + text: str, + column: int, + line: Optional[int] = None, + **extra, +): + if frameId is not None: + select_frame(frameId) + + column = import_column(column) + if line is None: + line = 1 + else: + line = import_line(line) + text = text.splitlines()[line - 1] + text = text[: column - 1] + mi_result = exec_mi_and_log("-complete", text) + result = [] + completion = None + if "completion" in mi_result: + completion = mi_result["completion"] + result.append({"label": completion, "length": len(completion)}) + # If `-complete' finds one match then `completion' and `matches' + # will contain the same one match. + if ( + completion is not None + and len(mi_result["matches"]) == 1 + and completion == mi_result["matches"][0] + ): + return {"targets": result} + for match in mi_result["matches"]: + result.append({"label": match, "length": len(match)}) + return {"targets": result} diff --git a/gdb/python/lib/gdb/dap/server.py b/gdb/python/lib/gdb/dap/server.py index 8fdf029..7139c79 100644 --- a/gdb/python/lib/gdb/dap/server.py +++ b/gdb/python/lib/gdb/dap/server.py @@ -49,6 +49,7 @@ _server = None # This is set by the initialize request and is used when rewriting # line numbers. _lines_start_at_1 = False +_columns_start_at_1 = False class DeferredRequest: @@ -593,6 +594,8 @@ def initialize(**args): _server.send_event_later("initialized") global _lines_start_at_1 _lines_start_at_1 = client_bool_capability("linesStartAt1", True) + global _columns_start_at_1 + _columns_start_at_1 = client_bool_capability("columnsStartAt1", True) return _capabilities.copy() @@ -698,7 +701,7 @@ def send_gdb_with_response(fn): return val -def export_line(line): +def export_line(line: int) -> int: """Rewrite LINE according to client capability. This applies the linesStartAt1 capability as needed, when sending a line number from gdb to the client.""" @@ -710,7 +713,7 @@ def export_line(line): return line -def import_line(line): +def import_line(line: int) -> int: """Rewrite LINE according to client capability. This applies the linesStartAt1 capability as needed, when the client sends a line number to gdb.""" @@ -720,3 +723,17 @@ def import_line(line): # the client starts at 0. line = line + 1 return line + + +def export_column(column: int) -> int: + """Rewrite COLUMN according to client capability. + This applies the columnsStartAt1 capability as needed, + when sending a column number from gdb to the client.""" + return column if _columns_start_at_1 else column - 1 + + +def import_column(column: int) -> int: + """Rewrite COLUMN according to client capability. + This applies the columnsStartAt1 capability as needed, + when the client sends a column number to gdb.""" + return column if _columns_start_at_1 else column + 1 diff --git a/gdb/regcache.c b/gdb/regcache.c index 5508778..ad72429 100644 --- a/gdb/regcache.c +++ b/gdb/regcache.c @@ -1911,32 +1911,13 @@ public: {} }; -/* Return true if regcache::cooked_{read,write}_test should be skipped for - GDBARCH. */ - -static bool -selftest_skiparch (struct gdbarch *gdbarch) -{ - const char *name = gdbarch_bfd_arch_info (gdbarch)->printable_name; - - /* Avoid warning: - Running selftest regcache::cooked_{read,write}_test::m68hc11. - warning: No frame soft register found in the symbol table. - Stack backtrace will not work. - We could instead capture the output and then filter out the warning, but - that seems more trouble than it's worth. */ - return (strcmp (name, "m68hc11") == 0 - || strcmp (name, "m68hc12") == 0 - || strcmp (name, "m68hc12:HCS12") == 0); -} - /* Test regcache::cooked_read gets registers from raw registers and memory instead of target to_{fetch,store}_registers. */ static void cooked_read_test (struct gdbarch *gdbarch) { - if (selftest_skiparch (gdbarch)) + if (selftest_skip_warning_arch (gdbarch)) return; scoped_mock_context<target_ops_no_register> mockctx (gdbarch); @@ -2074,7 +2055,7 @@ cooked_read_test (struct gdbarch *gdbarch) static void cooked_write_test (struct gdbarch *gdbarch) { - if (selftest_skiparch (gdbarch)) + if (selftest_skip_warning_arch (gdbarch)) return; /* Create a mock environment. A process_stratum target pushed. */ diff --git a/gdb/selftest-arch.c b/gdb/selftest-arch.c index 17eeba8..79889c0 100644 --- a/gdb/selftest-arch.c +++ b/gdb/selftest-arch.c @@ -108,5 +108,24 @@ reset () registers_changed (); reinit_frame_cache (); } + +/* See selftest-arch.h. */ + +bool +selftest_skip_warning_arch (struct gdbarch *gdbarch) +{ + const char *name = gdbarch_bfd_arch_info (gdbarch)->printable_name; + + /* Avoid warning: + Running selftest <test>::m68hc11. + warning: No frame soft register found in the symbol table. + Stack backtrace will not work. + We could instead capture the output and then filter out the warning, but + that seems more trouble than it's worth. */ + return (strcmp (name, "m68hc11") == 0 + || strcmp (name, "m68hc12") == 0 + || strcmp (name, "m68hc12:HCS12") == 0); +} + } /* namespace selftests */ #endif /* GDB_SELF_TEST */ diff --git a/gdb/selftest-arch.h b/gdb/selftest-arch.h index db11723..c6a85fa 100644 --- a/gdb/selftest-arch.h +++ b/gdb/selftest-arch.h @@ -29,6 +29,11 @@ namespace selftests extern void register_test_foreach_arch (const std::string &name, self_test_foreach_arch_function *function); + +/* Return true if GDBARCH should be skipped in some selftests to avoid + warnings. */ + +extern bool selftest_skip_warning_arch (struct gdbarch *gdbarch); } #endif /* GDB_SELFTEST_ARCH_H */ diff --git a/gdb/solib-darwin.c b/gdb/solib-darwin.c index 6c7d906..cbd89b1 100644 --- a/gdb/solib-darwin.c +++ b/gdb/solib-darwin.c @@ -146,24 +146,13 @@ struct lm_info_darwin final : public lm_info static CORE_ADDR lookup_symbol_from_bfd (bfd *abfd, const char *symname) { - long storage_needed; - asymbol **symbol_table; - unsigned int number_of_symbols; - unsigned int i; CORE_ADDR symaddr = 0; - storage_needed = bfd_get_symtab_upper_bound (abfd); + gdb::array_view<asymbol *> symbol_table + = gdb_bfd_canonicalize_symtab (abfd, false); - if (storage_needed <= 0) - return 0; - - symbol_table = (asymbol **) xmalloc (storage_needed); - number_of_symbols = bfd_canonicalize_symtab (abfd, symbol_table); - - for (i = 0; i < number_of_symbols; i++) + for (const asymbol *sym : symbol_table) { - asymbol *sym = symbol_table[i]; - if (strcmp (sym->name, symname) == 0 && (sym->section->flags & (SEC_CODE | SEC_DATA)) != 0) { @@ -172,7 +161,6 @@ lookup_symbol_from_bfd (bfd *abfd, const char *symname) break; } } - xfree (symbol_table); return symaddr; } diff --git a/gdb/solib.c b/gdb/solib.c index 7782c8d..0bbcb02 100644 --- a/gdb/solib.c +++ b/gdb/solib.c @@ -694,14 +694,17 @@ notify_solib_loaded (solib &so) /* Notify interpreters and observers that solib SO has been unloaded. When STILL_IN_USE is true, the objfile backing SO is still in use, this indicates that SO was loaded multiple times, but only mapped - in once (the mapping was reused). */ + in once (the mapping was reused). + + When SILENT is true, don't announce to the user if any breakpoints are + disabled as a result of unloading SO. */ static void notify_solib_unloaded (program_space *pspace, const solib &so, - bool still_in_use) + bool still_in_use, bool silent) { interps_notify_solib_unloaded (so, still_in_use); - gdb::observers::solib_unloaded.notify (pspace, so, still_in_use); + gdb::observers::solib_unloaded.notify (pspace, so, still_in_use, silent); } /* See solib.h. */ @@ -803,7 +806,7 @@ update_solib_list (int from_tty) /* Notify any observer that the shared object has been unloaded before we remove it from GDB's tables. */ notify_solib_unloaded (current_program_space, *gdb_iter, - still_in_use); + still_in_use, false); /* Unless the user loaded it explicitly, free SO's objfile. */ if (gdb_iter->objfile != nullptr @@ -1163,14 +1166,12 @@ clear_solib (program_space *pspace) { const solib_ops *ops = gdbarch_so_ops (current_inferior ()->arch ()); - disable_breakpoints_in_shlibs (pspace); - for (solib &so : pspace->so_list) { bool still_in_use = (so.objfile != nullptr && solib_used (pspace, so)); - notify_solib_unloaded (pspace, so, still_in_use); + notify_solib_unloaded (pspace, so, still_in_use, true); pspace->remove_target_sections (&so); }; @@ -1431,49 +1432,38 @@ CORE_ADDR gdb_bfd_lookup_symbol_from_symtab ( bfd *abfd, gdb::function_view<bool (const asymbol *)> match_sym) { - long storage_needed = bfd_get_symtab_upper_bound (abfd); CORE_ADDR symaddr = 0; + gdb::array_view<asymbol *> symbol_table + = gdb_bfd_canonicalize_symtab (abfd, false); - if (storage_needed > 0) + for (asymbol *sym : symbol_table) { - unsigned int i; - - gdb::def_vector<asymbol *> storage (storage_needed / sizeof (asymbol *)); - asymbol **symbol_table = storage.data (); - unsigned int number_of_symbols - = bfd_canonicalize_symtab (abfd, symbol_table); - - for (i = 0; i < number_of_symbols; i++) + if (match_sym (sym)) { - asymbol *sym = *symbol_table++; - - if (match_sym (sym)) + gdbarch *gdbarch = current_inferior ()->arch (); + symaddr = sym->value; + + /* Some ELF targets fiddle with addresses of symbols they + consider special. They use minimal symbols to do that + and this is needed for correct breakpoint placement, + but we do not have full data here to build a complete + minimal symbol, so just set the address and let the + targets cope with that. */ + if (bfd_get_flavour (abfd) == bfd_target_elf_flavour + && gdbarch_elf_make_msymbol_special_p (gdbarch)) { - gdbarch *gdbarch = current_inferior ()->arch (); - symaddr = sym->value; - - /* Some ELF targets fiddle with addresses of symbols they - consider special. They use minimal symbols to do that - and this is needed for correct breakpoint placement, - but we do not have full data here to build a complete - minimal symbol, so just set the address and let the - targets cope with that. */ - if (bfd_get_flavour (abfd) == bfd_target_elf_flavour - && gdbarch_elf_make_msymbol_special_p (gdbarch)) + struct minimal_symbol msym { - struct minimal_symbol msym - { - }; + }; - msym.set_value_address (symaddr); - gdbarch_elf_make_msymbol_special (gdbarch, sym, &msym); - symaddr = CORE_ADDR (msym.unrelocated_address ()); - } - - /* BFD symbols are section relative. */ - symaddr += sym->section->vma; - break; + msym.set_value_address (symaddr); + gdbarch_elf_make_msymbol_special (gdbarch, sym, &msym); + symaddr = CORE_ADDR (msym.unrelocated_address ()); } + + /* BFD symbols are section relative. */ + symaddr += sym->section->vma; + break; } } diff --git a/gdb/testsuite/gdb.base/filename-completion.exp b/gdb/testsuite/gdb.base/filename-completion.exp index 03ead59..a1dd974 100644 --- a/gdb/testsuite/gdb.base/filename-completion.exp +++ b/gdb/testsuite/gdb.base/filename-completion.exp @@ -381,11 +381,15 @@ proc run_mid_line_completion_tests { root cmd } { proc run_quoting_and_escaping_tests { root } { # Test all the commands which allow quoting of filenames, and # which require whitespace to be escaped in unquoted filenames. - foreach_with_prefix cmd { file exec-file symbol-file add-symbol-file \ - remove-symbol-file \ - "target core" "target exec" "target tfile" \ - "maint print c-tdesc" "compile file" \ - "save gdb-index" "save gdb-index -dwarf-5" } { + set all_cmds { file exec-file symbol-file add-symbol-file \ + remove-symbol-file \ + "target core" "target exec" "target tfile" \ + "maint print c-tdesc" "save gdb-index" + "save gdb-index -dwarf-5" } + if { [allow_compile_tests] } { + lappend all_cmds "compile file" + } + foreach_with_prefix cmd $all_cmds { # Try each test placing the filename as the first argument # then again with a quoted string immediately after the # command. This works because the filename completer will diff --git a/gdb/testsuite/gdb.base/gstack.exp b/gdb/testsuite/gdb.base/gstack.exp index 8df36b1..89be676 100644 --- a/gdb/testsuite/gdb.base/gstack.exp +++ b/gdb/testsuite/gdb.base/gstack.exp @@ -1,4 +1,4 @@ -# Copyright (C) 2024 Free Software Foundation, Inc. +# Copyright (C) 2024-2025 Free Software Foundation, Inc. # 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 @@ -62,8 +62,10 @@ if { ![gdb_assert { ![expr {$res < 0 || $res == ""}] } $test] } { set test "got backtrace" set saw_backtrace false set no_awk false +set location_re ${srcfile}:${decimal} + gdb_expect { - -i "$res" -re "#0 +(0x\[0-9a-f\]+ in )?main \(\).*\r\nGSTACK-END\r\n\$" { + -i "$res" -re "#0 +(0x\[0-9a-f\]+ in )?main \(\).*$location_re.*\r\nGSTACK-END\r\n\$" { set saw_backtrace true pass $test exp_continue diff --git a/gdb/testsuite/gdb.base/inline-frame-cycle-unwind.exp b/gdb/testsuite/gdb.base/inline-frame-cycle-unwind.exp index 45086f6..46561a9 100644 --- a/gdb/testsuite/gdb.base/inline-frame-cycle-unwind.exp +++ b/gdb/testsuite/gdb.base/inline-frame-cycle-unwind.exp @@ -72,77 +72,89 @@ gdb_continue_to_breakpoint "stop at test breakpoint" gdb_test_no_output "source ${pyfile}"\ "import python scripts" -# Check the unbroken stack. -gdb_test_sequence "bt" "backtrace when the unwind is left unbroken" { - "\\r\\n#0 \[^\r\n\]* inline_func \\(\\) at " - "\\r\\n#1 \[^\r\n\]* normal_func \\(\\) at " - "\\r\\n#2 \[^\r\n\]* inline_func \\(\\) at " - "\\r\\n#3 \[^\r\n\]* normal_func \\(\\) at " - "\\r\\n#4 \[^\r\n\]* inline_func \\(\\) at " - "\\r\\n#5 \[^\r\n\]* normal_func \\(\\) at " - "\\r\\n#6 \[^\r\n\]* main \\(\\) at " -} +# Test with and without filters. +foreach bt_cmd { "bt" "bt -no-filters" } { + with_test_prefix "$bt_cmd" { -with_test_prefix "cycle at level 5" { - # Arrange to introduce a stack cycle at frame 5. - gdb_test_no_output "python stop_at_level=5" - gdb_test "maint flush register-cache" \ - "Register cache flushed\\." - gdb_test_lines "bt" "backtrace when the unwind is broken at frame 5" \ - [multi_line \ - "#0 \[^\r\n\]* inline_func \\(\\) at \[^\r\n\]+" \ - "#1 \[^\r\n\]* normal_func \\(\\) at \[^\r\n\]+" \ - "#2 \[^\r\n\]* inline_func \\(\\) at \[^\r\n\]+" \ - "#3 \[^\r\n\]* normal_func \\(\\) at \[^\r\n\]+" \ - "#4 \[^\r\n\]* inline_func \\(\\) at \[^\r\n\]+" \ - "#5 \[^\r\n\]* normal_func \\(\\) at \[^\r\n\]+" \ - "Backtrace stopped: previous frame identical to this frame \\(corrupt stack\\?\\)"] -} + # Check the unbroken stack. + gdb_test_sequence "$bt_cmd" "backtrace when the unwind is left unbroken" { + "\\r\\n#0 \[^\r\n\]* inline_func \\(\\) at " + "\\r\\n#1 \[^\r\n\]* normal_func \\(\\) at " + "\\r\\n#2 \[^\r\n\]* inline_func \\(\\) at " + "\\r\\n#3 \[^\r\n\]* normal_func \\(\\) at " + "\\r\\n#4 \[^\r\n\]* inline_func \\(\\) at " + "\\r\\n#5 \[^\r\n\]* normal_func \\(\\) at " + "\\r\\n#6 \[^\r\n\]* main \\(\\) at " + } -with_test_prefix "cycle at level 3" { - # Arrange to introduce a stack cycle at frame 3. - gdb_test_no_output "python stop_at_level=3" - gdb_test "maint flush register-cache" \ - "Register cache flushed\\." - gdb_test_lines "bt" "backtrace when the unwind is broken at frame 3" \ - [multi_line \ - "#0 \[^\r\n\]* inline_func \\(\\) at \[^\r\n\]+" \ - "#1 \[^\r\n\]* normal_func \\(\\) at \[^\r\n\]+" \ - "#2 \[^\r\n\]* inline_func \\(\\) at \[^\r\n\]+" \ - "#3 \[^\r\n\]* normal_func \\(\\) at \[^\r\n\]+" \ - "Backtrace stopped: previous frame identical to this frame \\(corrupt stack\\?\\)"] -} + with_test_prefix "cycle at level 5" { + # Arrange to introduce a stack cycle at frame 5. + gdb_test_no_output "python stop_at_level=5" + gdb_test "maint flush register-cache" \ + "Register cache flushed\\." + gdb_test_lines "$bt_cmd" "backtrace when the unwind is broken at frame 5" \ + [multi_line \ + "#0 \[^\r\n\]* inline_func \\(\\) at \[^\r\n\]+" \ + "#1 \[^\r\n\]* normal_func \\(\\) at \[^\r\n\]+" \ + "#2 \[^\r\n\]* inline_func \\(\\) at \[^\r\n\]+" \ + "#3 \[^\r\n\]* normal_func \\(\\) at \[^\r\n\]+" \ + "#4 \[^\r\n\]* inline_func \\(\\) at \[^\r\n\]+" \ + "#5 \[^\r\n\]* normal_func \\(\\) at \[^\r\n\]+" \ + "Backtrace stopped: previous frame identical to this frame \\(corrupt stack\\?\\)"] + } -with_test_prefix "cycle at level 1" { - # Arrange to introduce a stack cycle at frame 1. - gdb_test_no_output "python stop_at_level=1" - gdb_test "maint flush register-cache" \ - "Register cache flushed\\." - gdb_test_lines "bt" "backtrace when the unwind is broken at frame 1" \ - [multi_line \ - "#0 \[^\r\n\]* inline_func \\(\\) at \[^\r\n\]+" \ - "#1 \[^\r\n\]* normal_func \\(\\) at \[^\r\n\]+" \ - "Backtrace stopped: previous frame identical to this frame \\(corrupt stack\\?\\)"] -} + with_test_prefix "cycle at level 3" { + # Arrange to introduce a stack cycle at frame 3. + gdb_test_no_output "python stop_at_level=3" + gdb_test "maint flush register-cache" \ + "Register cache flushed\\." + gdb_test_lines "$bt_cmd" "backtrace when the unwind is broken at frame 3" \ + [multi_line \ + "#0 \[^\r\n\]* inline_func \\(\\) at \[^\r\n\]+" \ + "#1 \[^\r\n\]* normal_func \\(\\) at \[^\r\n\]+" \ + "#2 \[^\r\n\]* inline_func \\(\\) at \[^\r\n\]+" \ + "#3 \[^\r\n\]* normal_func \\(\\) at \[^\r\n\]+" \ + "Backtrace stopped: previous frame identical to this frame \\(corrupt stack\\?\\)"] + } -# Flush the register cache (which also flushes the frame cache) so we -# get a full backtrace again, then switch on frame debugging and try -# to back trace. At one point this triggered an assertion. -gdb_test "maint flush register-cache" \ - "Register cache flushed\\." "" -gdb_test_no_output "set debug frame 1" -set ok 1 -gdb_test_multiple "bt" "backtrace with debugging on" { - -re "^$gdb_prompt $" { - gdb_assert { $ok } $gdb_test_name - } - -re "Python Exception <class 'gdb.error'>: \[^\r\n\]*\r\n" { - set ok 0 - exp_continue - } - -re "\[^\r\n\]+\r\n" { - exp_continue + with_test_prefix "cycle at level 1" { + # Arrange to introduce a stack cycle at frame 1. + gdb_test_no_output "python stop_at_level=1" + gdb_test "maint flush register-cache" \ + "Register cache flushed\\." + gdb_test_lines "$bt_cmd" "backtrace when the unwind is broken at frame 1" \ + [multi_line \ + "#0 \[^\r\n\]* inline_func \\(\\) at \[^\r\n\]+" \ + "#1 \[^\r\n\]* normal_func \\(\\) at \[^\r\n\]+" \ + "Backtrace stopped: previous frame identical to this frame \\(corrupt stack\\?\\)"] + } + + # Flush the register cache (which also flushes the frame cache) so we + # get a full backtrace again, then switch on frame debugging and try + # to back trace. At one point this triggered an assertion. + gdb_test "maint flush register-cache" \ + "Register cache flushed\\." "" + gdb_test_no_output "set debug frame 1" + set ok 1 + gdb_test_multiple "$bt_cmd" "backtrace with debugging on" { + -re "^$gdb_prompt $" { + gdb_assert { $ok } $gdb_test_name + } + -re "Python Exception <class 'gdb.error'>: \[^\r\n\]*\r\n" { + set ok 0 + exp_continue + } + -re "\[^\r\n\]+\r\n" { + exp_continue + } + } + gdb_test "p 1 + 2 + 3" " = 6" \ + "ensure GDB is still alive" + + # Prepare for the next iteration of the test loop + gdb_test_no_output "set debug frame 0" + gdb_test_no_output "python stop_at_level=None" + gdb_test "maint flush register-cache" \ + "Register cache flushed\\." "maint flush register-cache at (loop end)" } } -gdb_test "p 1 + 2 + 3" " = 6" \ - "ensure GDB is still alive" diff --git a/gdb/testsuite/gdb.base/options.exp b/gdb/testsuite/gdb.base/options.exp index a1ca39e..8760a91 100644 --- a/gdb/testsuite/gdb.base/options.exp +++ b/gdb/testsuite/gdb.base/options.exp @@ -99,21 +99,21 @@ proc make_cmd {variant} { # operand. proc expect_none {operand} { return "-flag 0 -xx1 0 -xx2 0 -bool 0 -enum xxx -uint-unl 0 -pint-unl 0\ - -string '' -filename '' -- $operand" + -string '' -filename '' -color magenta -- $operand" } # Return a string for the expected result of running "maint # test-options xxx", with -flag set. OPERAND is the expected operand. proc expect_flag {operand} { return "-flag 1 -xx1 0 -xx2 0 -bool 0 -enum xxx -uint-unl 0 -pint-unl 0\ - -string '' -filename '' -- $operand" + -string '' -filename '' -color magenta -- $operand" } # Return a string for the expected result of running "maint # test-options xxx", with -bool set. OPERAND is the expected operand. proc expect_bool {operand} { return "-flag 0 -xx1 0 -xx2 0 -bool 1 -enum xxx -uint-unl 0 -pint-unl 0\ - -string '' -filename '' -- $operand" + -string '' -filename '' -color magenta -- $operand" } # Return a string for the expected result of running "maint @@ -123,10 +123,10 @@ proc expect_bool {operand} { proc expect_integer {option val operand} { if {$option == "uinteger-unlimited"} { return "-flag 0 -xx1 0 -xx2 0 -bool 0 -enum xxx -uint-unl $val\ - -pint-unl 0 -string '' -filename '' -- $operand" + -pint-unl 0 -string '' -filename '' -color magenta -- $operand" } elseif {$option == "pinteger-unlimited"} { return "-flag 0 -xx1 0 -xx2 0 -bool 0 -enum xxx -uint-unl 0\ - -pint-unl $val -string '' -filename '' -- $operand" + -pint-unl $val -string '' -filename '' -color magenta -- $operand" } else { error "unsupported option: $option" } @@ -144,7 +144,7 @@ proc expect_string {str operand} { set str [string range $str 1 end-1] } return "-flag 0 -xx1 0 -xx2 0 -bool 0 -enum xxx -uint-unl 0 -pint-unl 0\ - -string '$str' -filename '' -- $operand" + -string '$str' -filename '' -color magenta -- $operand" } # Return a string for the expected result of running "maint @@ -159,11 +159,12 @@ proc expect_filename {str operand} { set str [string range $str 1 end-1] } return "-flag 0 -xx1 0 -xx2 0 -bool 0 -enum xxx -uint-unl 0 -pint-unl 0\ - -string '' -filename '$str' -- $operand" + -string '' -filename '$str' -color magenta -- $operand" } set all_options { "-bool" + "-color" "-enum" "-filename" "-flag" @@ -628,7 +629,7 @@ proc_with_prefix test-flag {variant} { # Extract twice the same flag, separated by one space. gdb_test "$cmd -xx1 -xx2 -xx1 -xx2 -xx1 -- non flags args" \ "-flag 0 -xx1 1 -xx2 1 -bool 0 -enum xxx -uint-unl 0 -pint-unl 0\ - -string '' -filename '' -- non flags args" + -string '' -filename '' -color magenta -- non flags args" # Extract 2 known flags in front of unknown flags. gdb_test "$cmd -xx1 -xx2 -a -b -c -xx1 --" \ diff --git a/gdb/testsuite/gdb.base/shlib-unload.exp b/gdb/testsuite/gdb.base/shlib-unload.exp index f3e8cce..9d47416 100644 --- a/gdb/testsuite/gdb.base/shlib-unload.exp +++ b/gdb/testsuite/gdb.base/shlib-unload.exp @@ -225,6 +225,75 @@ proc_with_prefix test_dprintf_with_rerun {} { "dprintf is non-pending after restart" } +# Check that we see breakpoint modified events (where appropriate) +# when the 'nosharedlibrary' command is used to unload all shared +# libraries. +# +# Also check that the 'nosharedlibrary' doesn't trigger a warning +# about shared library breakpoints being disabled. +proc_with_prefix test_silent_nosharedlib {} { + if { ![allow_python_tests] } { + unsupported "python support needed" + return + } + + foreach_with_prefix type { breakpoint dprintf } { + clean_restart $::binfile + + if {![runto_main]} { + return + } + + gdb_breakpoint $::srcfile:$::bp_line + gdb_continue_to_breakpoint "stop before dlclose" + + # Setup a dprintf or breakpoint in the shared library. + if { $type eq "breakpoint" } { + gdb_test "break foo" + } else { + gdb_test "dprintf foo,\"In foo\"" + } + + # Record the number of the b/p (or dprintf) we just inserted. + set bp_num [get_integer_valueof "\$bpnum" "*UNKNOWN*" \ + "get b/p number"] + + # Load Python library to track b/p modifications. + gdb_test_no_output "source $::pyfile" "import python scripts" + + # Initialise the b/p modified hash. Currently dprintf style + # breakpoints are not visible from Python, so the modification + # count will remain unchanged in that case. + gdb_test_no_output "python bp_modified_counts\[$bp_num\] = 0" + + # Discard symbols from all loaded shared libraries. + gdb_test_no_output "nosharedlibrary" + + # Check that our b/p is now showing as disabled. + if { $type eq "breakpoint" } { + set re \ + [list "$bp_num\\s+breakpoint\\s+keep\\s+y\\s+<PENDING>\\s+foo"] + set count 1 + } else { + set re \ + [list \ + "$bp_num\\s+dprintf\\s+keep\\s+y\\s+<PENDING>\\s+foo" \ + "\\s+printf \"In foo\""] + set count 0 + } + + gdb_test "info breakpoints $bp_num" \ + [multi_line "^Num\\s+Type\\s+Disp\\s+Enb\\s+Address\\s+What" \ + {*}$re] + + # Check we've seen the expected number of breakpoint modified + # events. Currently dprintf breakpoints are not visible from + # Python, so we will not see an event in that case. + gdb_test "python print(bp_modified_counts\[$bp_num\])" "^$count" + } +} + test_bp_modified_events test_dprintf_after_unload test_dprintf_with_rerun +test_silent_nosharedlib diff --git a/gdb/testsuite/gdb.compile/compile-cplus-anonymous.exp b/gdb/testsuite/gdb.compile/compile-cplus-anonymous.exp index 3e76d38..ddad628 100644 --- a/gdb/testsuite/gdb.compile/compile-cplus-anonymous.exp +++ b/gdb/testsuite/gdb.compile/compile-cplus-anonymous.exp @@ -23,6 +23,8 @@ require allow_cplus_tests require is_c_compiler_gcc +require allow_compile_tests + if {[prepare_for_testing $testfile $testfile $srcfile \ {debug nowarnings c++}]} { return -1 diff --git a/gdb/testsuite/gdb.compile/compile-cplus-array-decay.exp b/gdb/testsuite/gdb.compile/compile-cplus-array-decay.exp index 505a4e1..c7d15ce 100644 --- a/gdb/testsuite/gdb.compile/compile-cplus-array-decay.exp +++ b/gdb/testsuite/gdb.compile/compile-cplus-array-decay.exp @@ -23,6 +23,8 @@ require allow_cplus_tests require is_c_compiler_gcc +require allow_compile_tests + if {[prepare_for_testing $testfile $testfile $srcfile \ {debug nowarnings c++ additional_flags=-std=c++11}]} { return -1 diff --git a/gdb/testsuite/gdb.compile/compile-cplus-inherit.exp b/gdb/testsuite/gdb.compile/compile-cplus-inherit.exp index 1a5f60a..9ef1e83 100644 --- a/gdb/testsuite/gdb.compile/compile-cplus-inherit.exp +++ b/gdb/testsuite/gdb.compile/compile-cplus-inherit.exp @@ -23,6 +23,8 @@ require allow_cplus_tests require is_c_compiler_gcc +require allow_compile_tests + if {[prepare_for_testing $testfile $testfile $srcfile \ {debug nowarnings c++}]} { return -1 diff --git a/gdb/testsuite/gdb.compile/compile-cplus-member.exp b/gdb/testsuite/gdb.compile/compile-cplus-member.exp index 5ffbb30..ac9111c 100644 --- a/gdb/testsuite/gdb.compile/compile-cplus-member.exp +++ b/gdb/testsuite/gdb.compile/compile-cplus-member.exp @@ -23,6 +23,8 @@ require allow_cplus_tests require is_c_compiler_gcc +require allow_compile_tests + if {[prepare_for_testing $testfile $testfile $srcfile \ {debug nowarnings c++}]} { return -1 diff --git a/gdb/testsuite/gdb.compile/compile-cplus-method.exp b/gdb/testsuite/gdb.compile/compile-cplus-method.exp index 0a0e0fa..bcbfbb0 100644 --- a/gdb/testsuite/gdb.compile/compile-cplus-method.exp +++ b/gdb/testsuite/gdb.compile/compile-cplus-method.exp @@ -23,6 +23,8 @@ require allow_cplus_tests require is_c_compiler_gcc +require allow_compile_tests + if {[prepare_for_testing $testfile $testfile $srcfile \ {debug nowarnings c++}]} { return -1 diff --git a/gdb/testsuite/gdb.compile/compile-cplus-namespace.exp b/gdb/testsuite/gdb.compile/compile-cplus-namespace.exp index 3ab8ece..2abc366 100644 --- a/gdb/testsuite/gdb.compile/compile-cplus-namespace.exp +++ b/gdb/testsuite/gdb.compile/compile-cplus-namespace.exp @@ -23,6 +23,8 @@ require allow_cplus_tests require is_c_compiler_gcc +require allow_compile_tests + if {[prepare_for_testing $testfile $testfile $srcfile \ {debug nowarnings c++}]} { return -1 diff --git a/gdb/testsuite/gdb.compile/compile-cplus-nested.exp b/gdb/testsuite/gdb.compile/compile-cplus-nested.exp index 19efd4f..247d270 100644 --- a/gdb/testsuite/gdb.compile/compile-cplus-nested.exp +++ b/gdb/testsuite/gdb.compile/compile-cplus-nested.exp @@ -23,6 +23,8 @@ require allow_cplus_tests require is_c_compiler_gcc +require allow_compile_tests + if {[prepare_for_testing $testfile $testfile $srcfile \ {debug nowarnings c++}]} { return -1 diff --git a/gdb/testsuite/gdb.compile/compile-cplus-print.exp b/gdb/testsuite/gdb.compile/compile-cplus-print.exp index 594f94a..e4413f0 100644 --- a/gdb/testsuite/gdb.compile/compile-cplus-print.exp +++ b/gdb/testsuite/gdb.compile/compile-cplus-print.exp @@ -19,6 +19,8 @@ standard_testfile require is_c_compiler_gcc +require allow_compile_tests + set options {} if [test_compiler_info gcc*] { lappend options additional_flags=-g3 diff --git a/gdb/testsuite/gdb.compile/compile-cplus-virtual.exp b/gdb/testsuite/gdb.compile/compile-cplus-virtual.exp index 8761df5..a770208 100644 --- a/gdb/testsuite/gdb.compile/compile-cplus-virtual.exp +++ b/gdb/testsuite/gdb.compile/compile-cplus-virtual.exp @@ -23,6 +23,8 @@ require allow_cplus_tests require is_c_compiler_gcc +require allow_compile_tests + if {[prepare_for_testing $testfile $testfile $srcfile \ {debug nowarnings c++}]} { return -1 diff --git a/gdb/testsuite/gdb.compile/compile-cplus.exp b/gdb/testsuite/gdb.compile/compile-cplus.exp index 711f299..35ae692 100644 --- a/gdb/testsuite/gdb.compile/compile-cplus.exp +++ b/gdb/testsuite/gdb.compile/compile-cplus.exp @@ -19,6 +19,8 @@ standard_testfile .c compile-shlib.c compile-constvar.S compile-nodebug.c require is_c_compiler_gcc +require allow_compile_tests + set options {} if { [test_compiler_info gcc*] || [test_compiler_info clang*] } { lappend options additional_flags=-g3 diff --git a/gdb/testsuite/gdb.compile/compile-ifunc.exp b/gdb/testsuite/gdb.compile/compile-ifunc.exp index b004bd7..e490890 100644 --- a/gdb/testsuite/gdb.compile/compile-ifunc.exp +++ b/gdb/testsuite/gdb.compile/compile-ifunc.exp @@ -17,6 +17,8 @@ load_lib compile-support.exp require allow_ifunc_tests +require allow_compile_tests + standard_testfile require is_c_compiler_gcc diff --git a/gdb/testsuite/gdb.compile/compile-ops.exp b/gdb/testsuite/gdb.compile/compile-ops.exp index f75e02c..cfbe2b0 100644 --- a/gdb/testsuite/gdb.compile/compile-ops.exp +++ b/gdb/testsuite/gdb.compile/compile-ops.exp @@ -22,6 +22,8 @@ load_lib dwarf.exp # This test can only be run on targets which support DWARF-2 and use gas. require dwarf2_support +require allow_compile_tests + require is_c_compiler_gcc standard_testfile .c -dbg.S diff --git a/gdb/testsuite/gdb.compile/compile-print.exp b/gdb/testsuite/gdb.compile/compile-print.exp index f8f2297..61ce750 100644 --- a/gdb/testsuite/gdb.compile/compile-print.exp +++ b/gdb/testsuite/gdb.compile/compile-print.exp @@ -19,6 +19,8 @@ standard_testfile require is_c_compiler_gcc +require allow_compile_tests + if { [prepare_for_testing "failed to prepare" "$testfile"] } { return -1 } diff --git a/gdb/testsuite/gdb.compile/compile-setjmp.exp b/gdb/testsuite/gdb.compile/compile-setjmp.exp index f387a05..ad8732b 100644 --- a/gdb/testsuite/gdb.compile/compile-setjmp.exp +++ b/gdb/testsuite/gdb.compile/compile-setjmp.exp @@ -19,6 +19,8 @@ standard_testfile .c compile-setjmp-mod.c require is_c_compiler_gcc +require allow_compile_tests + if { [prepare_for_testing "failed to prepare" $testfile] } { return -1 } diff --git a/gdb/testsuite/gdb.compile/compile-tls.exp b/gdb/testsuite/gdb.compile/compile-tls.exp index 2f8dc5a..45e290e 100644 --- a/gdb/testsuite/gdb.compile/compile-tls.exp +++ b/gdb/testsuite/gdb.compile/compile-tls.exp @@ -19,6 +19,8 @@ standard_testfile .c require is_c_compiler_gcc +require allow_compile_tests + if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" \ executable {debug}] != "" } { return -1 diff --git a/gdb/testsuite/gdb.compile/compile.exp b/gdb/testsuite/gdb.compile/compile.exp index 2c2e321..5128dc62 100644 --- a/gdb/testsuite/gdb.compile/compile.exp +++ b/gdb/testsuite/gdb.compile/compile.exp @@ -15,6 +15,8 @@ load_lib compile-support.exp +require allow_compile_tests + standard_testfile .c compile-shlib.c compile-constvar.S compile-nodebug.c require is_c_compiler_gcc diff --git a/gdb/testsuite/gdb.mi/mi-dprintf-modified-lib.c b/gdb/testsuite/gdb.mi/mi-dprintf-modified-lib.c new file mode 100644 index 0000000..70fc328 --- /dev/null +++ b/gdb/testsuite/gdb.mi/mi-dprintf-modified-lib.c @@ -0,0 +1,22 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2025 Free Software Foundation, Inc. + + 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/>. */ + +int +foo (void) +{ + return 0; +} diff --git a/gdb/testsuite/gdb.mi/mi-dprintf-modified.c b/gdb/testsuite/gdb.mi/mi-dprintf-modified.c new file mode 100644 index 0000000..7a41adbac --- /dev/null +++ b/gdb/testsuite/gdb.mi/mi-dprintf-modified.c @@ -0,0 +1,55 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2025 Free Software Foundation, Inc. + + 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/>. */ + +#include <stdlib.h> + +#ifdef __WIN32__ +#include <windows.h> +#define dlopen(name, mode) LoadLibrary (TEXT (name)) +#ifdef _WIN32_WCE +# define dlsym(handle, func) GetProcAddress (handle, TEXT (func)) +#else +# define dlsym(handle, func) GetProcAddress (handle, func) +#endif +#define dlclose(handle) FreeLibrary (handle) +#else +#include <dlfcn.h> +#endif + +#include <assert.h> + +int +main (void) +{ + int res; + void *handle; + int (*func) (void); + int val = 0; + + handle = dlopen (SHLIB_NAME, RTLD_LAZY); /* Break here. */ + assert (handle != NULL); + + func = (int (*)(void)) dlsym (handle, "foo"); + assert (func != NULL); + + val += func (); + + res = dlclose (handle); + assert (res == 0); + + return val; +} diff --git a/gdb/testsuite/gdb.mi/mi-dprintf-modified.exp b/gdb/testsuite/gdb.mi/mi-dprintf-modified.exp new file mode 100644 index 0000000..c3e1bdf --- /dev/null +++ b/gdb/testsuite/gdb.mi/mi-dprintf-modified.exp @@ -0,0 +1,119 @@ +# Copyright 2025 Free Software Foundation, Inc. + +# 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/>. + +# Check that GDB doesn't emit a 'breakpoint-modified' notification for +# dprintf breakpoints when the dprintf commands haven't changed. +# +# GDB use to emit a 'breakpoint-modified' dprintf breakpoints each +# time the dprintf_breakpoint::re_set function was called as this +# would re-cacluate the dprintf command string, even though in most +# cases the calculated string was no different from the previous +# value. +# +# Then GDB got smarter and could recognise that the string had not +# changed, and so would skip the 'breakpoint-modified' notification. +# +# This test stops at a dlopen() call in the inferior and creates a +# dprintf breakpoint. Then we 'next' over the dlopen() which triggers +# a call to the ::re_set() functions. We check that there is no +# 'breakpoint-modified' event emitted for the dprintf breakpoint. + +load_lib mi-support.exp +set MIFLAGS "-i=mi" + +standard_testfile .c -lib.c + +# Build the library. +set libname ${testfile}-lib +set libfile [standard_output_file $libname] +if { [build_executable "build shlib" $libfile $srcfile2 {debug shlib}] == -1} { + return +} + +# Build the executable. +set opts [list debug shlib_load additional_flags=-DSHLIB_NAME=\"${libname}\"] +if { [build_executable "build exec" $binfile $srcfile $opts] == -1} { + return +} + +# The line number of the dlopen() call. +set bp_line [gdb_get_line_number "Break here" $srcfile] + +# Start the inferior. +mi_clean_restart $binfile +mi_runto_main + +# Place a breakpoint at the dlopen() line. +mi_create_breakpoint $srcfile:$bp_line "set breakpoint at dlopen call" \ + -disp keep -func main -file "\[^\r\n\]+/$srcfile" -line $bp_line + +# And run to the breakpoint. +mi_execute_to "exec-continue" "breakpoint-hit" main "" ".*/$srcfile" \ + $bp_line { "" "disp=\"keep\"" } "run to breakpoint" + +# Cleanup breakpoints. +mi_delete_breakpoints + +# Setup a dprintf breakpoint. +mi_gdb_test "-dprintf-insert --function main \"in main\"" \ + "\\^done,bkpt={.*}" "dprintf at main" + +set bpnum [mi_get_valueof "/d" "\$bpnum" "INVALID" \ + "get number for dprintf breakpoint"] + +# Use 'next' to step over loading the shared library. +mi_gdb_test "220-exec-next" ".*" "next over dlopen" + +# Now wait for the 'stopped' notification. While we wait we should +# see a 'library-loaded' notification for the loading of the shared +# library. +# +# In older versions of GDB we would also see a 'breakpoint-modified' +# notification for the dprintf breakpoint, but newer versions of GDB +# are smart enough to not emit this unnecessary notification. +set bp_re [mi_make_breakpoint -number $bpnum \ + -type dprintf -disp keep -enabled y -func main] +set saw_bp_modified false +set saw_lib_load false +set saw_stopped false +gdb_test_multiple "" "wait for 'next' to complete" { + -re "^=library-loaded,id=\[^\r\n\]+\r\n" { + set saw_lib_load true + exp_continue + } + + -re "^=breakpoint-modified,$bp_re\r\n" { + set saw_bp_modified true + exp_continue + } + + -re "^\\*stopped,reason=\"end-stepping-range\",\[^\r\n\]+\r\n" { + set saw_stopped true + exp_continue + } + + -re "^$mi_gdb_prompt$" { + gdb_assert { $saw_lib_load } \ + "$gdb_test_name, library was loaded" + gdb_assert { $saw_stopped } \ + "$gdb_test_name, saw stopped message" + gdb_assert { !$saw_bp_modified } \ + "$gdb_test_name, no breakpoint-modified" + } + + -re "^\[^\r\n\]+\r\n" { + exp_continue + } +} diff --git a/gdb/testsuite/gdb.rocm/precise-memory.cpp b/gdb/testsuite/gdb.rocm/precise-memory.cpp index 769b58a..7a8c37e 100644 --- a/gdb/testsuite/gdb.rocm/precise-memory.cpp +++ b/gdb/testsuite/gdb.rocm/precise-memory.cpp @@ -31,7 +31,17 @@ __global__ void kernel () { - __builtin_amdgcn_s_sleep (1); + + /* Simple kernel which loads from address 0 to trigger a pagefault. + When precise memory is not enabled, it is expected that the memory fault + is reported after the s_nop instruction. With precise-memory, the + exception should be reported on the s_nop. */ + asm volatile ("s_mov_b64 [s10, s11], 0\n" + "s_load_dword s12, [s10, s11]\n" + "s_nop 0" + : + : + : "s10", "s11", "s12"); } int diff --git a/gdb/testsuite/gdb.rocm/precise-memory.exp b/gdb/testsuite/gdb.rocm/precise-memory.exp index f423a11..8c39f80 100644 --- a/gdb/testsuite/gdb.rocm/precise-memory.exp +++ b/gdb/testsuite/gdb.rocm/precise-memory.exp @@ -39,18 +39,40 @@ proc do_test { } { "AMDGPU precise memory access reporting is off \\(currently disabled\\)." \ "show precise-memory setting in CLI before" - if {[hip_devices_support_precise_memory]} { - gdb_test_no_output "set amdgpu precise-memory on" - set cli_effective_value "enabled" - } else { - gdb_test "set amdgpu precise-memory on" \ - "warning: AMDGPU precise memory access reporting could not be enabled." - set cli_effective_value "disabled" + # Assume precise-memory is available, unless GDB reports otherwise. + gdb_test_multiple "set amdgpu precise-memory on" "" { + -re -wrap "warning: AMDGPU precise memory access reporting could not be enabled\\." { + set cli_effective_value "disabled" + pass $gdb_test_name + } + -re -wrap "^" { + set cli_effective_value "enabled" + pass $gdb_test_name + } } gdb_test "show amdgpu precise-memory" \ - "AMDGPU precise memory access reporting is on \\(currently ${cli_effective_value}\\)." \ + "AMDGPU precise memory access reporting is on \\(currently ${cli_effective_value}\\)\\." \ "show precise-memory setting in CLI after" + + if { $cli_effective_value eq "disabled" } { + return + } + + # Get to the begining of the GPU kernel without precise memory enabled. + with_test_prefix "goto gpu code" { + gdb_test_no_output "set amdgpu precise-memory off" + gdb_breakpoint "kernel" allow-pending + gdb_test "continue" "Thread ${::decimal}.* hit Breakpoint .*" + gdb_test_no_output "set amdgpu precise-memory on" + } + + # If precise-memory is available, run until a SIGSEGV is reported. At + # that point, the PC should point to the s_nop instruction (the one + # following the one which caused the memory violation). + gdb_test "continue" "Thread ${::decimal}\[^\r\n\]* received signal SIGSEGV, Segmentation fault.*" + + gdb_test "x/i \$pc" "=> ${::hex} <_Z6kernelv\\+${::decimal}>:\[ \t\]+s_nop\[ \t\]+0" } } diff --git a/gdb/testsuite/gdb.threads/access-mem-running-thread-exit.c b/gdb/testsuite/gdb.threads/access-mem-running-thread-exit.c index af05b13..e22bf12 100644 --- a/gdb/testsuite/gdb.threads/access-mem-running-thread-exit.c +++ b/gdb/testsuite/gdb.threads/access-mem-running-thread-exit.c @@ -97,6 +97,11 @@ thread_fn (void *arg) return NULL; } +static void +setup_done (void) +{ +} + int main (void) { @@ -104,6 +109,8 @@ main (void) global_var++; + setup_done (); + for (i = 0; i < 4; i++) { struct thread_arg *p; diff --git a/gdb/testsuite/gdb.threads/access-mem-running-thread-exit.exp b/gdb/testsuite/gdb.threads/access-mem-running-thread-exit.exp index 784f17f..42222c0 100644 --- a/gdb/testsuite/gdb.threads/access-mem-running-thread-exit.exp +++ b/gdb/testsuite/gdb.threads/access-mem-running-thread-exit.exp @@ -54,7 +54,7 @@ proc test { non_stop } { clean_restart ${binfile} } - if ![runto_main] { + if ![runto setup_done] { return -1 } @@ -76,7 +76,7 @@ proc test { non_stop } { # Start the second inferior. with_test_prefix "second inferior" { # With stub targets that do reload on run, if we let the new - # inferior share inferior 1's connection, runto_main would + # inferior share inferior 1's connection, runto would # fail because GDB is already connected to something, like # e.g. with --target_board=native-gdbserver: # @@ -86,10 +86,10 @@ proc test { non_stop } { # Already connected to a remote target. Disconnect? (y or n) # # Instead, start the inferior with no connection, and let - # gdb_load/runto_main spawn a new remote connection/gdbserver. + # gdb_load/runto spawn a new remote connection/gdbserver. # # OTOH, with extended-remote, we must let the new inferior - # reuse the current connection, so that runto_main below can + # reuse the current connection, so that runto below can # issue the "run" command, and have the inferior run on the # remote target. If we forced no connection, then "run" would # either fail if "set auto-connect-native-target" is on, like @@ -108,7 +108,7 @@ proc test { non_stop } { gdb_load $binfile - if ![runto_main] { + if ![runto setup_done] { return -1 } } diff --git a/gdb/testsuite/lib/ada.exp b/gdb/testsuite/lib/ada.exp index 0a1231b..e1a3a23 100644 --- a/gdb/testsuite/lib/ada.exp +++ b/gdb/testsuite/lib/ada.exp @@ -181,12 +181,16 @@ proc find_ada_tool {tool} { # compiler does not appear to be GCC, this will always return false. proc gnat_version_compare {op l2} { - set gccvers [gcc_major_version] - if {$gccvers == -1} { + set gnatmake [find_gnatmake] + set gnatmake [lindex [split $gnatmake] 0] + if {[catch {exec $gnatmake --version} output]} { + return 0 + } + if {![regexp {GNATMAKE ([0-9]+(\.[0-9]+)*)} $output match version]} { return 0 } - return [version_compare [split $gccvers .] $op $l2] + return [version_compare [split $version .] $op $l2] } # Return 1 if the GNAT runtime appears to have debug info. diff --git a/gdb/testsuite/lib/compile-support.exp b/gdb/testsuite/lib/compile-support.exp index aa8aaf3..6d7a4ce 100644 --- a/gdb/testsuite/lib/compile-support.exp +++ b/gdb/testsuite/lib/compile-support.exp @@ -45,6 +45,9 @@ proc _do_check_compile {expr} { # This appears to be a bug in the compiler plugin. set result "apparent compiler plugin bug" } + -re "This command is not supported." { + set result "compiler disabled at configure time" + } -re "\r\n$gdb_prompt $" { } } diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp index 3349da7..59967c7 100644 --- a/gdb/testsuite/lib/gdb.exp +++ b/gdb/testsuite/lib/gdb.exp @@ -2799,6 +2799,12 @@ gdb_caching_proc allow_python_tests {} { return [expr {[string first "--with-python" $output] != -1}] } +# Return a 1 if GDB was configured to support compile commands. +gdb_caching_proc allow_compile_tests {} { + set output [remote_exec host $::GDB "$::INTERNAL_GDBFLAGS -ex \"compile int x = 1\" -batch"] + return [expr {[string first "The program must be running" $output] != -1}] +} + # Return a 1 for configurations that use system readline rather than the # in-repo copy. @@ -3931,13 +3937,16 @@ gdb_caching_proc is_aarch32_target {} { return 0 } - set list {} - foreach reg \ - {r0 r1 r2 r3} { - lappend list "\tmov $reg, $reg" - } + return [gdb_can_simple_compile aarch32 { + int main (void) { + asm ("\tmov r0, r0"); + asm ("\tmov r1, r1"); + asm ("\tmov r2, r2"); + asm ("\tmov r3, r3"); - return [gdb_can_simple_compile aarch32 [join $list \n]] + return 0; + } + }] } # Return 1 if this target is an aarch64, either lp64 or ilp32. @@ -5940,6 +5949,23 @@ proc gdb_compile {source dest type options} { } } + # On AIX systems, until GCC 12 (maybe later), stabs was the default + # debug option, but we'd like to have dwarf instead. + # If we're running on one of those systems and debug was requested, + # but no explicit -g<format> option was given, use -gdwarf to force + # that as the debug info for the inferior. + # This list should be exhaustive: + set debug_format "btf|ctf|stabs|vms|coff|xcoff" + # Since additional_flags is a comma separated list, identify if there + # are other (optional) flags in the list. + set other_options "-\[a-zA-Z0-9\]*," + set full_regexp "^additional_flags=\($other_options\)*-g\($debug_format\)" + if { [istarget *-*-aix*] + && [lsearch -exact $options debug] != -1 + && [lsearch -regexp $options $full_regexp] == -1} { + lappend new_options "additional_flags=-gdwarf" + } + set shlib_found 0 set shlib_load 0 foreach opt $options { diff --git a/gdb/testsuite/lib/rocm.exp b/gdb/testsuite/lib/rocm.exp index 3eb51db..5164f1e 100644 --- a/gdb/testsuite/lib/rocm.exp +++ b/gdb/testsuite/lib/rocm.exp @@ -176,22 +176,3 @@ proc hip_devices_support_debug_multi_process {} { } return 1 } - -# Return true if all the devices on the host support precise memory. - -proc hip_devices_support_precise_memory {} { - set unsupported_targets \ - {gfx900 gfx906 gfx908 gfx1010 gfx1011 gfx1012 gfx1030 gfx1031 gfx1032} - - set targets [find_amdgpu_devices] - if { [llength $targets] == 0 } { - return 0 - } - - foreach target $targets { - if { [lsearch -exact $unsupported_targets $target] != -1 } { - return 0 - } - } - return 1 -} |