/* Symbol table lookup for the GNU debugger, GDB.
- Copyright (C) 1986-2021 Free Software Foundation, Inc.
+ Copyright (C) 1986-2022 Free Software Foundation, Inc.
This file is part of GDB.
#include "symfile.h"
#include "objfiles.h"
#include "gdbcmd.h"
-#include "gdb_regex.h"
+#include "gdbsupport/gdb_regex.h"
#include "expression.h"
#include "language.h"
#include "demangle.h"
#include "hashtab.h"
#include "typeprint.h"
-#include "gdb_obstack.h"
+#include "gdbsupport/gdb_obstack.h"
#include "block.h"
#include "dictionary.h"
/* See symtab.h. */
+call_site *
+compunit_symtab::find_call_site (CORE_ADDR pc) const
+{
+ if (m_call_site_htab == nullptr)
+ return nullptr;
+
+ CORE_ADDR delta
+ = this->objfile->section_offsets[COMPUNIT_BLOCK_LINE_SECTION (this)];
+ CORE_ADDR unrelocated_pc = pc - delta;
+
+ struct call_site call_site_local (unrelocated_pc, nullptr, nullptr);
+ void **slot
+ = htab_find_slot (m_call_site_htab, &call_site_local, NO_INSERT);
+ if (slot == nullptr)
+ return nullptr;
+
+ return (call_site *) *slot;
+}
+
+/* See symtab.h. */
+
+void
+compunit_symtab::set_call_site_htab (htab_t call_site_htab)
+{
+ gdb_assert (m_call_site_htab == nullptr);
+ m_call_site_htab = call_site_htab;
+}
+
+/* See symtab.h. */
+
struct symtab *
compunit_primary_filetab (const struct compunit_symtab *cust)
{
/* See symtab.h */
-char *
+gdb::unique_xmalloc_ptr<char>
symbol_find_demangled_name (struct general_symbol_info *gsymbol,
const char *mangled)
{
- char *demangled = NULL;
+ gdb::unique_xmalloc_ptr<char> demangled;
int i;
if (gsymbol->language () == language_unknown)
linkage_name_copy = linkage_name;
if (demangled_name.get () == nullptr)
- demangled_name.reset
- (symbol_find_demangled_name (this, linkage_name_copy.data ()));
+ demangled_name
+ = symbol_find_demangled_name (this, linkage_name_copy.data ());
/* Suppose we have demangled_name==NULL, copy_name==0, and
linkage_name_copy==linkage_name. In this case, we already have the
/* If the cache hasn't been created yet, avoid creating one. */
cache = symbol_cache_key.get (pspace);
if (cache == NULL)
- printf_filtered (" empty, no stats available\n");
+ printf_filtered (" empty, no stats available\n");
else
symbol_cache_stats (cache);
}
if (fallback == -1)
fallback = idx;
- if (obj_section_addr (s) - offset <= addr
- && addr < obj_section_endaddr (s) - offset)
+ if (s->addr () - offset <= addr && addr < s->endaddr () - offset)
{
ginfo->set_section_index (idx);
return;
lookup, so we can always binary search. */
if (lang == language_cplus)
{
- char *demangled_name = gdb_demangle (name, DMGL_ANSI | DMGL_PARAMS);
+ gdb::unique_xmalloc_ptr<char> demangled_name
+ = gdb_demangle (name, DMGL_ANSI | DMGL_PARAMS);
if (demangled_name != NULL)
- return storage.set_malloc_ptr (demangled_name);
+ return storage.set_malloc_ptr (std::move (demangled_name));
/* If we were given a non-mangled name, canonicalize it
according to the language (so far only for C++). */
}
else if (lang == language_d)
{
- char *demangled_name = d_demangle (name, 0);
+ gdb::unique_xmalloc_ptr<char> demangled_name = d_demangle (name, 0);
if (demangled_name != NULL)
- return storage.set_malloc_ptr (demangled_name);
+ return storage.set_malloc_ptr (std::move (demangled_name));
}
else if (lang == language_go)
{
- char *demangled_name
+ gdb::unique_xmalloc_ptr<char> demangled_name
= language_def (language_go)->demangle_symbol (name, 0);
if (demangled_name != NULL)
- return storage.set_malloc_ptr (demangled_name);
+ return storage.set_malloc_ptr (std::move (demangled_name));
}
return name;
for (i = type->num_fields () - 1; i >= TYPE_N_BASECLASSES (type); i--)
{
- const char *t_field_name = TYPE_FIELD_NAME (type, i);
+ const char *t_field_name = type->field (i).name ();
if (t_field_name && (strcmp_iw (t_field_name, name) == 0))
{
/* I'm not really sure that type of this can ever
be typedefed; just be safe. */
t = check_typedef (t);
- if (t->code () == TYPE_CODE_PTR || TYPE_IS_REFERENCE (t))
+ if (t->is_pointer_or_reference ())
t = TYPE_TARGET_TYPE (t);
if (t->code () != TYPE_CODE_STRUCT
}
\f
-/* What part to match in a file name. */
+/* See class declaration. */
-struct filename_partial_match_opts
+info_sources_filter::info_sources_filter (match_on match_type,
+ const char *regexp)
+ : m_match_type (match_type),
+ m_regexp (regexp)
{
- /* Only match the directory name part. */
- bool dirname = false;
+ /* Setup the compiled regular expression M_C_REGEXP based on M_REGEXP. */
+ if (m_regexp != nullptr && *m_regexp != '\0')
+ {
+ gdb_assert (m_regexp != nullptr);
- /* Only match the basename part. */
- bool basename = false;
-};
+ int cflags = REG_NOSUB;
+#ifdef HAVE_CASE_INSENSITIVE_FILE_SYSTEM
+ cflags |= REG_ICASE;
+#endif
+ m_c_regexp.emplace (m_regexp, cflags, _("Invalid regexp"));
+ }
+}
-/* Data structure to maintain printing state for output_source_filename. */
+/* See class declaration. */
-struct output_source_filename_data
+bool
+info_sources_filter::matches (const char *fullname) const
{
- /* Output only filenames matching REGEXP. */
- std::string regexp;
- gdb::optional<compiled_regex> c_regexp;
- /* Possibly only match a part of the filename. */
- filename_partial_match_opts partial_match;
+ /* Does it match regexp? */
+ if (m_c_regexp.has_value ())
+ {
+ const char *to_match;
+ std::string dirname;
+ switch (m_match_type)
+ {
+ case match_on::DIRNAME:
+ dirname = ldirname (fullname);
+ to_match = dirname.c_str ();
+ break;
+ case match_on::BASENAME:
+ to_match = lbasename (fullname);
+ break;
+ case match_on::FULLNAME:
+ to_match = fullname;
+ break;
+ default:
+ gdb_assert_not_reached ("bad m_match_type");
+ }
+
+ if (m_c_regexp->exec (to_match, 0, NULL, 0) != 0)
+ return false;
+ }
- /* Cache of what we've seen so far. */
- struct filename_seen_cache *filename_seen_cache;
+ return true;
+}
- /* Flag of whether we're printing the first one. */
- int first;
+/* Data structure to maintain the state used for printing the results of
+ the 'info sources' command. */
- /* Worker for sources_info. Force line breaks at ,'s.
- NAME is the name to print. */
- void output (const char *name);
+struct output_source_filename_data
+{
+ /* Create an object for displaying the results of the 'info sources'
+ command to UIOUT. FILTER must remain valid and unchanged for the
+ lifetime of this object as this object retains a reference to FILTER. */
+ output_source_filename_data (struct ui_out *uiout,
+ const info_sources_filter &filter)
+ : m_filter (filter),
+ m_uiout (uiout)
+ { /* Nothing. */ }
+
+ DISABLE_COPY_AND_ASSIGN (output_source_filename_data);
+
+ /* Reset enough state of this object so we can match against a new set of
+ files. The existing regular expression is retained though. */
+ void reset_output ()
+ {
+ m_first = true;
+ m_filename_seen_cache.clear ();
+ }
+
+ /* Worker for sources_info, outputs the file name formatted for either
+ cli or mi (based on the current_uiout). In cli mode displays
+ FULLNAME with a comma separating this name from any previously
+ printed name (line breaks are added at the comma). In MI mode
+ outputs a tuple containing DISP_NAME (the files display name),
+ FULLNAME, and EXPANDED_P (true when this file is from a fully
+ expanded symtab, otherwise false). */
+ void output (const char *disp_name, const char *fullname, bool expanded_p);
/* An overload suitable for use as a callback to
quick_symbol_functions::map_symbol_filenames. */
void operator() (const char *filename, const char *fullname)
{
- output (fullname != nullptr ? fullname : filename);
+ /* The false here indicates that this file is from an unexpanded
+ symtab. */
+ output (filename, fullname, false);
}
+
+ /* Return true if at least one filename has been printed (after a call to
+ output) since either this object was created, or the last call to
+ reset_output. */
+ bool printed_filename_p () const
+ {
+ return !m_first;
+ }
+
+private:
+
+ /* Flag of whether we're printing the first one. */
+ bool m_first = true;
+
+ /* Cache of what we've seen so far. */
+ filename_seen_cache m_filename_seen_cache;
+
+ /* How source filename should be filtered. */
+ const info_sources_filter &m_filter;
+
+ /* The object to which output is sent. */
+ struct ui_out *m_uiout;
};
+/* See comment in class declaration above. */
+
void
-output_source_filename_data::output (const char *name)
+output_source_filename_data::output (const char *disp_name,
+ const char *fullname,
+ bool expanded_p)
{
/* Since a single source file can result in several partial symbol
tables, we need to avoid printing it more than once. Note: if
situation. I'm not sure whether this can also happen for
symtabs; it doesn't hurt to check. */
- /* Was NAME already seen? */
- if (filename_seen_cache->seen (name))
- {
- /* Yes; don't print it again. */
- return;
- }
-
- /* Does it match regexp? */
- if (c_regexp.has_value ())
- {
- const char *to_match;
- std::string dirname;
+ /* Was NAME already seen? If so, then don't print it again. */
+ if (m_filename_seen_cache.seen (fullname))
+ return;
- if (partial_match.dirname)
- {
- dirname = ldirname (name);
- to_match = dirname.c_str ();
- }
- else if (partial_match.basename)
- to_match = lbasename (name);
- else
- to_match = name;
+ /* If the filter rejects this file then don't print it. */
+ if (!m_filter.matches (fullname))
+ return;
- if (c_regexp->exec (to_match, 0, NULL, 0) != 0)
- return;
- }
+ ui_out_emit_tuple ui_emitter (m_uiout, nullptr);
/* Print it and reset *FIRST. */
- if (! first)
- printf_filtered (", ");
- first = 0;
+ if (!m_first)
+ m_uiout->text (", ");
+ m_first = false;
wrap_here ("");
- fputs_styled (name, file_name_style.style (), gdb_stdout);
+ if (m_uiout->is_mi_like_p ())
+ {
+ m_uiout->field_string ("file", disp_name, file_name_style.style ());
+ if (fullname != nullptr)
+ m_uiout->field_string ("fullname", fullname,
+ file_name_style.style ());
+ m_uiout->field_string ("debug-fully-read",
+ (expanded_p ? "true" : "false"));
+ }
+ else
+ {
+ if (fullname == nullptr)
+ fullname = disp_name;
+ m_uiout->field_string ("fullname", fullname,
+ file_name_style.style ());
+ }
}
+/* For the 'info sources' command, what part of the file names should we be
+ matching the user supplied regular expression against? */
+
+struct filename_partial_match_opts
+{
+ /* Only match the directory name part. */
+ bool dirname = false;
+
+ /* Only match the basename part. */
+ bool basename = false;
+};
+
using isrc_flag_option_def
= gdb::option::flag_option_def<filename_partial_match_opts>;
return {{info_sources_option_defs}, isrc_opts};
}
-/* Prints the header message for the source files that will be printed
- with the matching info present in DATA. SYMBOL_MSG is a message
- that tells what will or has been done with the symbols of the
- matching source files. */
-
-static void
-print_info_sources_header (const char *symbol_msg,
- const struct output_source_filename_data *data)
-{
- puts_filtered (symbol_msg);
- if (!data->regexp.empty ())
- {
- if (data->partial_match.dirname)
- printf_filtered (_("(dirname matching regular expression \"%s\")"),
- data->regexp.c_str ());
- else if (data->partial_match.basename)
- printf_filtered (_("(basename matching regular expression \"%s\")"),
- data->regexp.c_str ());
- else
- printf_filtered (_("(filename matching regular expression \"%s\")"),
- data->regexp.c_str ());
- }
- puts_filtered ("\n");
-}
-
/* Completer for "info sources". */
static void
return;
}
-static void
-info_sources_command (const char *args, int from_tty)
-{
- struct output_source_filename_data data;
-
- if (!have_full_symbols () && !have_partial_symbols ())
- {
- error (_("No symbol table is loaded. Use the \"file\" command."));
- }
-
- filename_seen_cache filenames_seen;
-
- auto group = make_info_sources_options_def_group (&data.partial_match);
-
- gdb::option::process_options
- (&args, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_ERROR, group);
+/* See symtab.h. */
- if (args != NULL && *args != '\000')
- data.regexp = args;
+void
+info_sources_worker (struct ui_out *uiout,
+ bool group_by_objfile,
+ const info_sources_filter &filter)
+{
+ output_source_filename_data data (uiout, filter);
- data.filename_seen_cache = &filenames_seen;
- data.first = 1;
+ ui_out_emit_list results_emitter (uiout, "files");
+ gdb::optional<ui_out_emit_tuple> output_tuple;
+ gdb::optional<ui_out_emit_list> sources_list;
- if (data.partial_match.dirname && data.partial_match.basename)
- error (_("You cannot give both -basename and -dirname to 'info sources'."));
- if ((data.partial_match.dirname || data.partial_match.basename)
- && data.regexp.empty ())
- error (_("Missing REGEXP for 'info sources'."));
-
- if (data.regexp.empty ())
- data.c_regexp.reset ();
- else
- {
- int cflags = REG_NOSUB;
-#ifdef HAVE_CASE_INSENSITIVE_FILE_SYSTEM
- cflags |= REG_ICASE;
-#endif
- data.c_regexp.emplace (data.regexp.c_str (), cflags,
- _("Invalid regexp"));
- }
-
- print_info_sources_header
- (_("Source files for which symbols have been read in:\n"), &data);
+ gdb_assert (group_by_objfile || uiout->is_mi_like_p ());
for (objfile *objfile : current_program_space->objfiles ())
{
+ if (group_by_objfile)
+ {
+ output_tuple.emplace (uiout, nullptr);
+ uiout->field_string ("filename", objfile_name (objfile));
+ uiout->text (":\n");
+ bool debug_fully_readin = !objfile->has_unexpanded_symtabs ();
+ if (uiout->is_mi_like_p ())
+ {
+ const char *debug_info_state;
+ if (objfile_has_symbols (objfile))
+ {
+ if (debug_fully_readin)
+ debug_info_state = "fully-read";
+ else
+ debug_info_state = "partially-read";
+ }
+ else
+ debug_info_state = "none";
+ current_uiout->field_string ("debug-info", debug_info_state);
+ }
+ else
+ {
+ if (!debug_fully_readin)
+ uiout->text ("(Full debug information has not yet been read "
+ "for this file.)\n");
+ if (!objfile_has_symbols (objfile))
+ uiout->text ("(Objfile has no debug information.)\n");
+ uiout->text ("\n");
+ }
+ sources_list.emplace (uiout, "sources");
+ }
+
for (compunit_symtab *cu : objfile->compunits ())
{
for (symtab *s : compunit_filetabs (cu))
{
+ const char *file = symtab_to_filename_for_display (s);
const char *fullname = symtab_to_fullname (s);
-
- data.output (fullname);
+ data.output (file, fullname, true);
}
}
+
+ if (group_by_objfile)
+ {
+ objfile->map_symbol_filenames (data, true /* need_fullname */);
+ if (data.printed_filename_p ())
+ uiout->text ("\n\n");
+ data.reset_output ();
+ sources_list.reset ();
+ output_tuple.reset ();
+ }
}
- printf_filtered ("\n\n");
- print_info_sources_header
- (_("Source files for which symbols will be read in on demand:\n"), &data);
+ if (!group_by_objfile)
+ {
+ data.reset_output ();
+ map_symbol_filenames (data, true /*need_fullname*/);
+ }
+}
+
+/* Implement the 'info sources' command. */
+
+static void
+info_sources_command (const char *args, int from_tty)
+{
+ if (!have_full_symbols () && !have_partial_symbols ())
+ error (_("No symbol table is loaded. Use the \"file\" command."));
+
+ filename_partial_match_opts match_opts;
+ auto group = make_info_sources_options_def_group (&match_opts);
+ gdb::option::process_options
+ (&args, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_ERROR, group);
+
+ if (match_opts.dirname && match_opts.basename)
+ error (_("You cannot give both -basename and -dirname to 'info sources'."));
+
+ const char *regex = nullptr;
+ if (args != NULL && *args != '\000')
+ regex = args;
+
+ if ((match_opts.dirname || match_opts.basename) && regex == nullptr)
+ error (_("Missing REGEXP for 'info sources'."));
- filenames_seen.clear ();
- data.first = 1;
- map_symbol_filenames (data, true /*need_fullname*/);
- printf_filtered ("\n");
+ info_sources_filter::match_on match_type;
+ if (match_opts.dirname)
+ match_type = info_sources_filter::match_on::DIRNAME;
+ else if (match_opts.basename)
+ match_type = info_sources_filter::match_on::BASENAME;
+ else
+ match_type = info_sources_filter::match_on::FULLNAME;
+
+ info_sources_filter filter (match_type, regex);
+ info_sources_worker (current_uiout, true, filter);
}
/* Compare FILE against all the entries of FILENAMES. If BASENAMES is
enum search_domain kind = m_kind;
bool found_msymbol = false;
+ auto do_file_match = [&] (const char *filename, bool basenames)
+ {
+ return file_matches (filename, filenames, basenames);
+ };
+ gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher = nullptr;
+ if (!filenames.empty ())
+ file_matcher = do_file_match;
+
objfile->expand_symtabs_matching
- ([&] (const char *filename, bool basenames)
- {
- return file_matches (filename, filenames, basenames);
- },
+ (file_matcher,
&lookup_name_info::match_any (),
[&] (const char *symname)
{
{
bool quiet = false;
bool exclude_minsyms = false;
- char *type_regexp = nullptr;
-
- ~info_vars_funcs_options ()
- {
- xfree (type_regexp);
- }
+ std::string type_regexp;
};
/* The options used by the 'info variables' and 'info functions'
gdb::option::string_option_def<info_vars_funcs_options> {
"t",
- [] (info_vars_funcs_options *opt) { return &opt->type_regexp;
- },
+ [] (info_vars_funcs_options *opt) { return &opt->type_regexp; },
nullptr, /* show_cmd_cb */
nullptr /* set_doc */
}
if (args != nullptr && *args == '\0')
args = nullptr;
- symtab_symbol_info (opts.quiet, opts.exclude_minsyms, args, VARIABLES_DOMAIN,
- opts.type_regexp, from_tty);
+ symtab_symbol_info
+ (opts.quiet, opts.exclude_minsyms, args, VARIABLES_DOMAIN,
+ opts.type_regexp.empty () ? nullptr : opts.type_regexp.c_str (),
+ from_tty);
}
/* Implement the 'info functions' command. */
if (args != nullptr && *args == '\0')
args = nullptr;
- symtab_symbol_info (opts.quiet, opts.exclude_minsyms, args,
- FUNCTIONS_DOMAIN, opts.type_regexp, from_tty);
+ symtab_symbol_info
+ (opts.quiet, opts.exclude_minsyms, args, FUNCTIONS_DOMAIN,
+ opts.type_regexp.empty () ? nullptr : opts.type_regexp.c_str (),
+ from_tty);
}
/* Holds the -q option for the 'info types' command. */
if (c == TYPE_CODE_UNION || c == TYPE_CODE_STRUCT)
for (j = TYPE_N_BASECLASSES (t); j < t->num_fields (); j++)
- if (TYPE_FIELD_NAME (t, j))
+ if (t->field (j).name ())
completion_list_add_name (tracker, sym->language (),
- TYPE_FIELD_NAME (t, j),
+ t->field (j).name (),
lookup_name, text, word);
}
}
"ARM/Thumb C/C++ Compiler, RVCT",
"ARM C/C++ Compiler, RVCT"
};
- int i;
if (producer == NULL)
return false;
- for (i = 0; i < ARRAY_SIZE (arm_idents); i++)
- if (startswith (producer, arm_idents[i]))
+ for (const char *ident : arm_idents)
+ if (startswith (producer, ident))
return true;
return false;
struct info_modules_var_func_options
{
bool quiet = false;
- char *type_regexp = nullptr;
- char *module_regexp = nullptr;
-
- ~info_modules_var_func_options ()
- {
- xfree (type_regexp);
- xfree (module_regexp);
- }
+ std::string type_regexp;
+ std::string module_regexp;
};
/* The options used by 'info module variables' and 'info module functions'
if (args != nullptr && *args == '\0')
args = nullptr;
- info_module_subcommand (opts.quiet, opts.module_regexp, args,
- opts.type_regexp, FUNCTIONS_DOMAIN);
+ info_module_subcommand
+ (opts.quiet,
+ opts.module_regexp.empty () ? nullptr : opts.module_regexp.c_str (), args,
+ opts.type_regexp.empty () ? nullptr : opts.type_regexp.c_str (),
+ FUNCTIONS_DOMAIN);
}
/* Implements the 'info module variables' command. */
if (args != nullptr && *args == '\0')
args = nullptr;
- info_module_subcommand (opts.quiet, opts.module_regexp, args,
- opts.type_regexp, VARIABLES_DOMAIN);
+ info_module_subcommand
+ (opts.quiet,
+ opts.module_regexp.empty () ? nullptr : opts.module_regexp.c_str (), args,
+ opts.type_regexp.empty () ? nullptr : opts.type_regexp.c_str (),
+ VARIABLES_DOMAIN);
}
/* Command completer for 'info module ...' sub-commands. */
REGEXP is given. The optional flag -q disables printing of headers."));
set_cmd_completer_handle_brkchars (c, info_types_command_completer);
- const auto info_sources_opts = make_info_sources_options_def_group (nullptr);
+ const auto info_sources_opts
+ = make_info_sources_options_def_group (nullptr);
static std::string info_sources_help
= gdb::option::build_help (_("\