/* 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 . */ #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 (current_program_space, 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 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::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_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); }