Files
binutils-gdb/gdb/mi/mi-symbol-cmds.c
Andrew Burgess 379876d22a gdb/mi: Add new commands -symbol-info-{functions,variables,types}
Add new MI commands -symbol-info-functions, -symbol-info-variables,
and -symbol-info-types which correspond to the CLI commands 'info
functions', 'info variables', and 'info types' respectively.

gdb/ChangeLog:

	* mi/mi-cmds.c (mi_cmds): Add '-symbol-info-functions',
	'-symbol-info-variables', and '-symbol-info-types'.
	* mi/mi-cmds.h (mi_cmd_symbol_info_functions): Declare.
	(mi_cmd_symbol_info_variables): Declare.
	(mi_cmd_symbol_info_types): Declare.
	* mi/mi-symbol-cmds.c: Add 'source.h' and 'mi-getopt.h' includes.
	(mi_info_one_symbol_details): New function.
	(class mi_symbol_info_emitter): New class.
	(mi_symbol_info): New function.
	(mi_info_functions_or_variables): New function.
	(mi_cmd_symbol_info_functions): New function.
	(mi_cmd_symbol_info_types): New function.
	(mi_cmd_symbol_info_variables): New function.
	* NEWS: Mention new commands.

gdb/testsuite/ChangeLog:

	* gdb.mi/mi-sym-info-1.c: New file.
	* gdb.mi/mi-sym-info-2.c: New file.
	* gdb.mi/mi-sym-info.exp: New file.

gdb/doc/ChangeLog:

	* doc/gdb.texinfo (GDB/MI Symbol Query): Document new MI command
	-symbol-info-functions, -symbol-info-types, and
	-symbol-info-variables.

Change-Id: Ic2fc6a6750bbce91cdde2344791014e5ef45642d
2019-11-20 23:11:41 -05:00

368 lines
11 KiB
C

/* MI Command Set - symbol commands.
Copyright (C) 2003-2019 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 "defs.h"
#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, char **argv, int argc)
{
struct gdbarch *gdbarch;
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. */
gdbarch = get_objfile_arch (SYMTAB_OBJFILE (s));
ui_out_emit_list list_emitter (uiout, "lines");
if (SYMTAB_LINETABLE (s) != NULL && SYMTAB_LINETABLE (s)->nitems > 0)
for (i = 0; i < SYMTAB_LINETABLE (s)->nitems; i++)
{
ui_out_emit_tuple tuple_emitter (uiout, NULL);
uiout->field_core_addr ("pc", gdbarch, SYMTAB_LINETABLE (s)->item[i].pc);
uiout->field_signed ("line", SYMTAB_LINETABLE (s)->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
mi_info_one_symbol_details (enum search_domain kind,
struct symbol *sym, int block)
{
struct ui_out *uiout = current_uiout;
ui_out_emit_tuple tuple_emitter (uiout, NULL);
if (SYMBOL_LINE (sym) != 0)
uiout->field_unsigned ("line", SYMBOL_LINE (sym));
uiout->field_string ("name", SYMBOL_PRINT_NAME (sym));
if (kind == FUNCTIONS_DOMAIN || kind == VARIABLES_DOMAIN)
{
string_file tmp_stream;
type_print (SYMBOL_TYPE (sym), "", &tmp_stream, -1);
uiout->field_string ("type", tmp_stream.string ());
std::string str = symbol_to_info_string (sym, block, kind);
uiout->field_string ("description", str.c_str ());
}
}
/* This class is used to produce the nested structure of tuples and lists
required to present the results of the MI_SYMBOL_INFO function. */
class mi_symbol_info_emitter
{
/* When printing debug symbols we need to track the last symtab so we can
spot when we have entered a new one. */
const symtab *m_last_symtab;
/* The ui_out to which output will be sent. */
struct ui_out *m_uiout;
/* The outer container for all the matched symbols. */
ui_out_emit_tuple m_outer_symbols;
/* The order of these optional emitters is critical as they will be
deleted in reverse order, which is important as these are popped from
the uiout stack as they are destroyed. */
gdb::optional<ui_out_emit_list> m_debug_emitter;
gdb::optional<ui_out_emit_tuple> m_symtab_emitter;
gdb::optional<ui_out_emit_list> m_symbols_emitter;
gdb::optional<ui_out_emit_list> m_nondebug_emitter;
/* Called when we might want to print our first nondebug symbol in order
to shutdown any debug symbol printing that might be in progress. */
void maybe_finish_debug_output ()
{
/* If the debug emitter is in use. */
if (m_debug_emitter.has_value ())
{
/* Then we should have a symbols list inside a symtab tuple also
currently in use. */
gdb_assert (m_symbols_emitter.has_value ());
gdb_assert (m_symtab_emitter.has_value ());
/* Shut down the symbols list, symtab tuple, and debug list
emitters (in that order). We are now back to the level of the
outer_symbols tuple ready to (possibly) start a nondebug list,
though that is not done here. */
m_symbols_emitter.reset ();
m_symtab_emitter.reset ();
m_debug_emitter.reset ();
}
}
/* Return true if the nondebug emitter has been put in place. */
bool have_started_nondebug_symbol_output () const
{
return m_nondebug_emitter.has_value ();
}
/* Called before we print every nondebug symbol. If this is the first
nondebug symbol to be printed then it will setup the emitters required
to print nondebug symbols. */
void maybe_start_nondebug_symbol_output ()
{
if (!have_started_nondebug_symbol_output ())
m_nondebug_emitter.emplace (m_uiout, "nondebug");
}
/* Actually output one nondebug symbol, puts a tuple emitter in place
and then outputs the fields for this msymbol. */
void output_nondebug_symbol (const struct bound_minimal_symbol &msymbol)
{
struct gdbarch *gdbarch = get_objfile_arch (msymbol.objfile);
ui_out_emit_tuple tuple_emitter (m_uiout, NULL);
m_uiout->field_core_addr ("address", gdbarch,
BMSYMBOL_VALUE_ADDRESS (msymbol));
m_uiout->field_string ("name", MSYMBOL_PRINT_NAME (msymbol.minsym));
}
/* Called before we print every debug symbol. If this is the first debug
symbol to be printed then it will setup the top level of emitters
required to print debug symbols. */
void maybe_start_debug_symbol_output ()
{
if (!m_debug_emitter.has_value ())
m_debug_emitter.emplace (m_uiout, "debug");
}
/* Called before we print every debug symbol, S is the symtab for the
symbol to be printed. If S is different to the last symtab we printed
for then we close down the emitters for the last symtab, and create
new emitters for this new symtab. */
void setup_emitters_for_symtab (symtab *s)
{
if (s != m_last_symtab)
{
/* Reset a possible previous symbol list within a symtab. */
m_symbols_emitter.reset ();
m_symtab_emitter.reset ();
/* Start a new symtab and symbol list within the symtab. */
m_symtab_emitter.emplace (m_uiout, nullptr);
m_uiout->field_string ("filename",
symtab_to_filename_for_display (s));
m_uiout->field_string ("fullname", symtab_to_fullname (s));
m_symbols_emitter.emplace (m_uiout, "symbols");
/* Record the current symtab. */
m_last_symtab = s;
}
}
public:
/* Constructor. */
mi_symbol_info_emitter (struct ui_out *uiout)
: m_last_symtab (nullptr),
m_uiout (uiout),
m_outer_symbols (uiout, "symbols")
{ /* Nothing. */ }
/* Output P a symbol found by searching for symbols of type KIND. */
void output (const symbol_search &p, enum search_domain kind)
{
if (p.msymbol.minsym != NULL)
{
/* If this is the first nondebug symbol, and we have previous
outputted a debug symbol then we need to close down all of the
emitters related to printing debug symbols. */
maybe_finish_debug_output ();
/* If this is the first nondebug symbol then we need to create the
emitters related to printing nondebug symbols. */
maybe_start_nondebug_symbol_output ();
/* We are no safe to emit the nondebug symbol. */
output_nondebug_symbol (p.msymbol);
}
else
{
/* All debug symbols should appear in the list before all
non-debug symbols. */
gdb_assert (!have_started_nondebug_symbol_output ());
/* If this is the first debug symbol then we need to create the
outer level of emitters related to printing debug symbols. */
maybe_start_debug_symbol_output ();
/* Ensure the correct emitters are in place to emit this debug
symbol. */
setup_emitters_for_symtab (symbol_symtab (p.symbol));
/* Emit information for this debug symbol. */
mi_info_one_symbol_details (kind, p.symbol, p.block);
}
}
};
/* This is the guts of the commands '-symbol-info-functions',
'-symbol-info-variables', and '-symbol-info-types'. It calls
search_symbols to find all matches and then prints the matching
[m]symbols in an MI structured format. This is similar to
symtab_symbol_info in symtab.c. All the arguments are used to
initialise a SEARCH_SYMBOLS_SPEC, see symtab.h for a description of
their meaning. */
static void
mi_symbol_info (enum search_domain kind, const char *regexp,
const char *t_regexp, bool exclude_minsyms)
{
/* Must make sure that if we're interrupted, symbols gets freed. */
global_symbol_searcher sym_search (kind, regexp, t_regexp, exclude_minsyms);
std::vector<symbol_search> symbols = sym_search.search ();
mi_symbol_info_emitter emitter (current_uiout);
for (const symbol_search &p : symbols)
{
QUIT;
emitter.output (p, kind);
}
}
/* 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 (enum search_domain kind, char **argv, int argc)
{
const char *regexp = nullptr;
const char *t_regexp = nullptr;
bool exclude_minsyms = true;
enum opt
{
INCLUDE_NONDEBUG_OPT, TYPE_REGEXP_OPT, NAME_REGEXP_OPT
};
static const struct mi_opt opts[] =
{
{"-include-nondebug" , INCLUDE_NONDEBUG_OPT, 0},
{"-type", TYPE_REGEXP_OPT, 1},
{"-name", NAME_REGEXP_OPT, 1},
{ 0, 0, 0 }
};
int oind = 0;
char *oarg = nullptr;
while (1)
{
const char *cmd_string
= ((kind == FUNCTIONS_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;
}
}
mi_symbol_info (kind, regexp, t_regexp, exclude_minsyms);
}
/* Implement -symbol-info-functions command. */
void
mi_cmd_symbol_info_functions (const char *command, char **argv, int argc)
{
mi_info_functions_or_variables (FUNCTIONS_DOMAIN, argv, argc);
}
/* Implement -symbol-info-types command. */
void
mi_cmd_symbol_info_types (const char *command, char **argv, int argc)
{
const char *regexp = nullptr;
enum opt
{
NAME_REGEXP_OPT
};
static const struct mi_opt opts[] =
{
{"-name", NAME_REGEXP_OPT, 1},
{ 0, 0, 0 }
};
int oind = 0;
char *oarg = nullptr;
while (1)
{
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;
}
}
mi_symbol_info (TYPES_DOMAIN, regexp, nullptr, true);
}
/* Implement -symbol-info-variables command. */
void
mi_cmd_symbol_info_variables (const char *command, char **argv, int argc)
{
mi_info_functions_or_variables (VARIABLES_DOMAIN, argv, argc);
}