| /* MI Command Set - symbol commands. |
| Copyright (C) 2003-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 "mi-cmds.h" |
| #include "symtab.h" |
| #include "objfiles.h" |
| #include "ui-out.h" |
| #include "source.h" |
| #include "mi-getopt.h" |
| |
| /* Print the list of all pc addresses and lines of code for the |
| provided (full or base) source file name. The entries are sorted |
| in ascending PC order. */ |
| |
| void |
| mi_cmd_symbol_list_lines (const char *command, const char *const *argv, |
| int argc) |
| { |
| struct gdbarch *gdbarch; |
| const char *filename; |
| struct symtab *s; |
| int i; |
| struct ui_out *uiout = current_uiout; |
| |
| if (argc != 1) |
| error (_("-symbol-list-lines: Usage: SOURCE_FILENAME")); |
| |
| filename = argv[0]; |
| s = lookup_symtab (filename); |
| |
| if (s == NULL) |
| error (_("-symbol-list-lines: Unknown source file name.")); |
| |
| /* Now, dump the associated line table. The pc addresses are |
| already sorted by increasing values in the symbol table, so no |
| need to perform any other sorting. */ |
| |
| struct objfile *objfile = s->compunit ()->objfile (); |
| gdbarch = objfile->arch (); |
| |
| ui_out_emit_list list_emitter (uiout, "lines"); |
| if (s->linetable () != NULL && s->linetable ()->nitems > 0) |
| for (i = 0; i < s->linetable ()->nitems; i++) |
| { |
| ui_out_emit_tuple tuple_emitter (uiout, NULL); |
| uiout->field_core_addr ("pc", gdbarch, |
| s->linetable ()->item[i].pc (objfile)); |
| uiout->field_signed ("line", s->linetable ()->item[i].line); |
| } |
| } |
| |
| /* Used by the -symbol-info-* and -symbol-info-module-* commands to print |
| information about the symbol SYM in a block of index BLOCK (either |
| GLOBAL_BLOCK or STATIC_BLOCK). KIND is the kind of symbol we searched |
| for in order to find SYM, which impact which fields are displayed in the |
| results. */ |
| |
| static void |
| output_debug_symbol (ui_out *uiout, domain_search_flags kind, |
| struct symbol *sym, int block) |
| { |
| ui_out_emit_tuple tuple_emitter (uiout, NULL); |
| |
| if (sym->line () != 0) |
| uiout->field_unsigned ("line", sym->line ()); |
| uiout->field_string ("name", sym->print_name ()); |
| |
| if ((kind & (SEARCH_FUNCTION_DOMAIN | SEARCH_VAR_DOMAIN)) != 0) |
| { |
| string_file tmp_stream; |
| type_print (sym->type (), "", &tmp_stream, -1); |
| uiout->field_string ("type", tmp_stream.string ()); |
| |
| std::string str = symbol_to_info_string (sym, block); |
| uiout->field_string ("description", str); |
| } |
| } |
| |
| /* Actually output one nondebug symbol, puts a tuple emitter in place |
| and then outputs the fields for this msymbol. */ |
| |
| static void |
| output_nondebug_symbol (ui_out *uiout, const bound_minimal_symbol &msymbol) |
| { |
| struct gdbarch *gdbarch = msymbol.objfile->arch (); |
| ui_out_emit_tuple tuple_emitter (uiout, NULL); |
| |
| uiout->field_core_addr ("address", gdbarch, |
| msymbol.value_address ()); |
| uiout->field_string ("name", msymbol.minsym->print_name ()); |
| } |
| |
| /* This is the guts of the commands '-symbol-info-functions', |
| '-symbol-info-variables', and '-symbol-info-types'. It searches for |
| symbols matching KING, NAME_REGEXP, TYPE_REGEXP, and EXCLUDE_MINSYMS, |
| and then prints the matching [m]symbols in an MI structured format. */ |
| |
| static void |
| mi_symbol_info (domain_search_flags kind, const char *name_regexp, |
| const char *type_regexp, bool exclude_minsyms, |
| size_t max_results) |
| { |
| global_symbol_searcher sym_search (kind, name_regexp); |
| sym_search.set_symbol_type_regexp (type_regexp); |
| sym_search.set_exclude_minsyms (exclude_minsyms); |
| sym_search.set_max_search_results (max_results); |
| std::vector<symbol_search> symbols = sym_search.search (); |
| ui_out *uiout = current_uiout; |
| int i = 0; |
| |
| ui_out_emit_tuple outer_symbols_emitter (uiout, "symbols"); |
| |
| /* Debug symbols are placed first. */ |
| if (i < symbols.size () && symbols[i].msymbol.minsym == nullptr) |
| { |
| ui_out_emit_list debug_symbols_list_emitter (uiout, "debug"); |
| |
| /* As long as we have debug symbols... */ |
| while (i < symbols.size () && symbols[i].msymbol.minsym == nullptr) |
| { |
| symtab *symtab = symbols[i].symbol->symtab (); |
| ui_out_emit_tuple symtab_tuple_emitter (uiout, nullptr); |
| |
| uiout->field_string ("filename", |
| symtab_to_filename_for_display (symtab)); |
| uiout->field_string ("fullname", symtab_to_fullname (symtab)); |
| |
| ui_out_emit_list symbols_list_emitter (uiout, "symbols"); |
| |
| /* As long as we have debug symbols from this symtab... */ |
| for (; (i < symbols.size () |
| && symbols[i].msymbol.minsym == nullptr |
| && symbols[i].symbol->symtab () == symtab); |
| ++i) |
| { |
| symbol_search &s = symbols[i]; |
| |
| output_debug_symbol (uiout, kind, s.symbol, s.block); |
| } |
| } |
| } |
| |
| /* Non-debug symbols are placed after. */ |
| if (i < symbols.size ()) |
| { |
| ui_out_emit_list nondebug_symbols_list_emitter (uiout, "nondebug"); |
| |
| /* As long as we have nondebug symbols... */ |
| for (; i < symbols.size (); i++) |
| { |
| gdb_assert (symbols[i].msymbol.minsym != nullptr); |
| output_nondebug_symbol (uiout, symbols[i].msymbol); |
| } |
| } |
| } |
| |
| /* Helper to parse the option text from an -max-results argument and return |
| the parsed value. If the text can't be parsed then an error is thrown. */ |
| |
| static size_t |
| parse_max_results_option (const char *arg) |
| { |
| char *ptr; |
| long long val = strtoll (arg, &ptr, 10); |
| if (arg == ptr || *ptr != '\0' || val > SIZE_MAX || val < 0) |
| error (_("invalid value for --max-results argument")); |
| size_t max_results = (size_t) val; |
| |
| return max_results; |
| } |
| |
| /* Helper for mi_cmd_symbol_info_{functions,variables} - depending on KIND. |
| Processes command line options from ARGV and ARGC. */ |
| |
| static void |
| mi_info_functions_or_variables (domain_search_flags kind, |
| const char *const *argv, int argc) |
| { |
| size_t max_results = SIZE_MAX; |
| const char *regexp = nullptr; |
| const char *t_regexp = nullptr; |
| bool exclude_minsyms = true; |
| |
| enum opt |
| { |
| INCLUDE_NONDEBUG_OPT, TYPE_REGEXP_OPT, NAME_REGEXP_OPT, MAX_RESULTS_OPT |
| }; |
| static const struct mi_opt opts[] = |
| { |
| {"-include-nondebug" , INCLUDE_NONDEBUG_OPT, 0}, |
| {"-type", TYPE_REGEXP_OPT, 1}, |
| {"-name", NAME_REGEXP_OPT, 1}, |
| {"-max-results", MAX_RESULTS_OPT, 1}, |
| { 0, 0, 0 } |
| }; |
| |
| int oind = 0; |
| const char *oarg = nullptr; |
| |
| while (1) |
| { |
| const char *cmd_string |
| = ((kind == SEARCH_FUNCTION_DOMAIN) |
| ? "-symbol-info-functions" : "-symbol-info-variables"); |
| int opt = mi_getopt (cmd_string, argc, argv, opts, &oind, &oarg); |
| if (opt < 0) |
| break; |
| switch ((enum opt) opt) |
| { |
| case INCLUDE_NONDEBUG_OPT: |
| exclude_minsyms = false; |
| break; |
| case TYPE_REGEXP_OPT: |
| t_regexp = oarg; |
| break; |
| case NAME_REGEXP_OPT: |
| regexp = oarg; |
| break; |
| case MAX_RESULTS_OPT: |
| max_results = parse_max_results_option (oarg); |
| break; |
| } |
| } |
| |
| mi_symbol_info (kind, regexp, t_regexp, exclude_minsyms, max_results); |
| } |
| |
| /* Type for an iterator over a vector of module_symbol_search results. */ |
| typedef std::vector<module_symbol_search>::const_iterator |
| module_symbol_search_iterator; |
| |
| /* Helper for mi_info_module_functions_or_variables. Display the results |
| from ITER up to END or until we find a symbol that is in a different |
| module, or in a different symtab than the first symbol we print. Update |
| and return the new value for ITER. */ |
| static module_symbol_search_iterator |
| output_module_symbols_in_single_module_and_file |
| (struct ui_out *uiout, module_symbol_search_iterator iter, |
| const module_symbol_search_iterator end, domain_search_flags kind) |
| { |
| /* The symbol for the module in which the first result resides. */ |
| const symbol *first_module_symbol = iter->first.symbol; |
| |
| /* The symbol for the first result, and the symtab in which it resides. */ |
| const symbol *first_result_symbol = iter->second.symbol; |
| symtab *first_symbtab = first_result_symbol->symtab (); |
| |
| /* Formatted output. */ |
| ui_out_emit_tuple current_file (uiout, nullptr); |
| uiout->field_string ("filename", |
| symtab_to_filename_for_display (first_symbtab)); |
| uiout->field_string ("fullname", symtab_to_fullname (first_symbtab)); |
| ui_out_emit_list item_list (uiout, "symbols"); |
| |
| /* Repeatedly output result symbols until either we run out of symbols, |
| we change module, or we change symtab. */ |
| for (; (iter != end |
| && first_module_symbol == iter->first.symbol |
| && first_symbtab == iter->second.symbol->symtab ()); |
| ++iter) |
| output_debug_symbol (uiout, kind, iter->second.symbol, |
| iter->second.block); |
| |
| return iter; |
| } |
| |
| /* Helper for mi_info_module_functions_or_variables. Display the results |
| from ITER up to END or until we find a symbol that is in a different |
| module than the first symbol we print. Update and return the new value |
| for ITER. */ |
| static module_symbol_search_iterator |
| output_module_symbols_in_single_module |
| (struct ui_out *uiout, module_symbol_search_iterator iter, |
| const module_symbol_search_iterator end, domain_search_flags kind) |
| { |
| gdb_assert (iter->first.symbol != nullptr); |
| gdb_assert (iter->second.symbol != nullptr); |
| |
| /* The symbol for the module in which the first result resides. */ |
| const symbol *first_module_symbol = iter->first.symbol; |
| |
| /* Create output formatting. */ |
| ui_out_emit_tuple module_tuple (uiout, nullptr); |
| uiout->field_string ("module", first_module_symbol->print_name ()); |
| ui_out_emit_list files_list (uiout, "files"); |
| |
| /* The results are sorted so that symbols within the same file are next |
| to each other in the list. Calling the output function once will |
| print all results within a single file. We keep calling the output |
| function until we change module. */ |
| while (iter != end && first_module_symbol == iter->first.symbol) |
| iter = output_module_symbols_in_single_module_and_file (uiout, iter, |
| end, kind); |
| return iter; |
| } |
| |
| /* Core of -symbol-info-module-functions and -symbol-info-module-variables. |
| KIND indicates what we are searching for, and ARGV and ARGC are the |
| command line options passed to the MI command. */ |
| |
| static void |
| mi_info_module_functions_or_variables (domain_search_flags kind, |
| const char *const *argv, int argc) |
| { |
| const char *module_regexp = nullptr; |
| const char *regexp = nullptr; |
| const char *type_regexp = nullptr; |
| |
| /* Process the command line options. */ |
| |
| enum opt |
| { |
| MODULE_REGEXP_OPT, TYPE_REGEXP_OPT, NAME_REGEXP_OPT |
| }; |
| static const struct mi_opt opts[] = |
| { |
| {"-module", MODULE_REGEXP_OPT, 1}, |
| {"-type", TYPE_REGEXP_OPT, 1}, |
| {"-name", NAME_REGEXP_OPT, 1}, |
| { 0, 0, 0 } |
| }; |
| |
| int oind = 0; |
| const char *oarg = nullptr; |
| |
| while (1) |
| { |
| const char *cmd_string |
| = ((kind == SEARCH_FUNCTION_DOMAIN) |
| ? "-symbol-info-module-functions" |
| : "-symbol-info-module-variables"); |
| int opt = mi_getopt (cmd_string, argc, argv, opts, &oind, &oarg); |
| if (opt < 0) |
| break; |
| switch ((enum opt) opt) |
| { |
| case MODULE_REGEXP_OPT: |
| module_regexp = oarg; |
| break; |
| case TYPE_REGEXP_OPT: |
| type_regexp = oarg; |
| break; |
| case NAME_REGEXP_OPT: |
| regexp = oarg; |
| break; |
| } |
| } |
| |
| std::vector<module_symbol_search> module_symbols |
| = search_module_symbols (module_regexp, regexp, type_regexp, kind); |
| |
| struct ui_out *uiout = current_uiout; |
| ui_out_emit_list all_matching_symbols (uiout, "symbols"); |
| |
| /* The results in the module_symbols list are ordered so symbols in the |
| same module are next to each other. Repeatedly call the output |
| function to print sequences of symbols that are in the same module |
| until we have no symbols left to print. */ |
| module_symbol_search_iterator iter = module_symbols.begin (); |
| const module_symbol_search_iterator end = module_symbols.end (); |
| while (iter != end) |
| iter = output_module_symbols_in_single_module (uiout, iter, end, kind); |
| } |
| |
| /* Implement -symbol-info-functions command. */ |
| |
| void |
| mi_cmd_symbol_info_functions (const char *command, const char *const *argv, |
| int argc) |
| { |
| mi_info_functions_or_variables (SEARCH_FUNCTION_DOMAIN, argv, argc); |
| } |
| |
| /* Implement -symbol-info-module-functions command. */ |
| |
| void |
| mi_cmd_symbol_info_module_functions (const char *command, |
| const char *const *argv, int argc) |
| { |
| mi_info_module_functions_or_variables (SEARCH_FUNCTION_DOMAIN, argv, argc); |
| } |
| |
| /* Implement -symbol-info-module-variables command. */ |
| |
| void |
| mi_cmd_symbol_info_module_variables (const char *command, |
| const char *const *argv, int argc) |
| { |
| mi_info_module_functions_or_variables (SEARCH_VAR_DOMAIN, argv, argc); |
| } |
| |
| /* Implement -symbol-inf-modules command. */ |
| |
| void |
| mi_cmd_symbol_info_modules (const char *command, const char *const *argv, |
| int argc) |
| { |
| size_t max_results = SIZE_MAX; |
| const char *regexp = nullptr; |
| |
| enum opt |
| { |
| NAME_REGEXP_OPT, MAX_RESULTS_OPT |
| }; |
| static const struct mi_opt opts[] = |
| { |
| {"-name", NAME_REGEXP_OPT, 1}, |
| {"-max-results", MAX_RESULTS_OPT, 1}, |
| { 0, 0, 0 } |
| }; |
| |
| int oind = 0; |
| const char *oarg = nullptr; |
| |
| while (1) |
| { |
| int opt = mi_getopt ("-symbol-info-modules", argc, argv, opts, |
| &oind, &oarg); |
| if (opt < 0) |
| break; |
| switch ((enum opt) opt) |
| { |
| case NAME_REGEXP_OPT: |
| regexp = oarg; |
| break; |
| case MAX_RESULTS_OPT: |
| max_results = parse_max_results_option (oarg); |
| break; |
| } |
| } |
| |
| mi_symbol_info (SEARCH_MODULE_DOMAIN, regexp, nullptr, true, max_results); |
| } |
| |
| /* Implement -symbol-info-types command. */ |
| |
| void |
| mi_cmd_symbol_info_types (const char *command, const char *const *argv, |
| int argc) |
| { |
| size_t max_results = SIZE_MAX; |
| const char *regexp = nullptr; |
| |
| enum opt |
| { |
| NAME_REGEXP_OPT, MAX_RESULTS_OPT |
| }; |
| static const struct mi_opt opts[] = |
| { |
| {"-name", NAME_REGEXP_OPT, 1}, |
| {"-max-results", MAX_RESULTS_OPT, 1}, |
| { 0, 0, 0 } |
| }; |
| |
| int oind = 0; |
| const char *oarg = nullptr; |
| |
| while (true) |
| { |
| int opt = mi_getopt ("-symbol-info-types", argc, argv, opts, |
| &oind, &oarg); |
| if (opt < 0) |
| break; |
| switch ((enum opt) opt) |
| { |
| case NAME_REGEXP_OPT: |
| regexp = oarg; |
| break; |
| case MAX_RESULTS_OPT: |
| max_results = parse_max_results_option (oarg); |
| break; |
| } |
| } |
| |
| mi_symbol_info (SEARCH_TYPE_DOMAIN | SEARCH_STRUCT_DOMAIN, regexp, nullptr, |
| true, max_results); |
| } |
| |
| /* Implement -symbol-info-variables command. */ |
| |
| void |
| mi_cmd_symbol_info_variables (const char *command, const char *const *argv, |
| int argc) |
| { |
| mi_info_functions_or_variables (SEARCH_VAR_DOMAIN, argv, argc); |
| } |