Files
binutils-gdb/gdb/mi/mi-cmd-break.c
Pedro Alves a20714ff39 Make "break foo" find "A::foo", A::B::foo", etc. [C++ and wild matching]
This patch teaches GDB about setting breakpoints in all scopes
(namespaces and classes) by default.

Here's a contrived example:

  (gdb) b func<tab>
  (anonymous namespace)::A::function()            Bn::(anonymous namespace)::B::function()        function(int, int)
  (anonymous namespace)::B::function()            Bn::(anonymous namespace)::function()           gdb::(anonymous namespace)::A::function()
  (anonymous namespace)::B::function() const      Bn::(anonymous namespace)::function(int, int)   gdb::(anonymous namespace)::function()
  (anonymous namespace)::function()               Bn::B::func()                                   gdb::(anonymous namespace)::function(int, int)
  (anonymous namespace)::function(int, int)       Bn::B::function()                               gdb::A::func()
  A::func()                                       Bn::func()                                      gdb::A::function()
  A::function()                                   Bn::function()                                  gdb::func()
  B::func()                                       Bn::function(int, int)                          gdb::function()
  B::function()                                   Bn::function(long)                              gdb::function(int, int)
  B::function() const                             func()                                          gdb::function(long)
  B::function_const() const                       function()
  (gdb) b function
  Breakpoint 1 at 0x4005ce: function. (26 locations)

  (gdb) b B::function<tab>
  (anonymous namespace)::B::function()        B::function() const                         Bn::B::function()
  (anonymous namespace)::B::function() const  B::function_const() const
  B::function()                               Bn::(anonymous namespace)::B::function()
  (gdb) b B::function
  Breakpoint 1 at 0x40072c: B::function. (6 locations)

To get back the original behavior of interpreting the function name as
a fully-qualified name, you can use the new "-qualified" (or "-q")
option/flag (added by this commit).  For example:

 (gdb) b B::function
 (anonymous namespace)::B::function()        B::function() const                         Bn::B::function()
 (anonymous namespace)::B::function() const  B::function_const() const
 B::function()                               Bn::(anonymous namespace)::B::function()

vs:

 (gdb) b -qualified B::function
 B::function()              B::function() const        B::function_const() const

I've chosen "-qualified" / "-q" because "-f" (for "full" or
"fully-qualified") is already taken for "-function".

Note: the "-qualified" option works with both linespecs and explicit
locations.  I.e., these are equivalent:

 (gdb) b -q func
 (gdb) b -q -f func

and so are these:

 (gdb) b -q filename.cc:func
 (gdb) b -q -s filename.cc -f func
 (gdb) b -s filename.cc -q -f func
 (gdb) b -s filename.cc -f func -q

To better understand why I consider wild matching the better default,
consider what happens when we get to the point when _all_ of GDB is
wrapped under "namespace gdb {}".  I have a patch series that does
that, and when I started debugging that GDB, I immediately became
frustrated.  You'd have to write "b gdb::internal_error", "b
gdb::foo", "b gdb::bar", etc. etc., which gets annoying pretty
quickly.  OTOH, consider how this makes it very easy to set
breakpoints in classes wrapped in anonymous namespaces.  You just
don't think of them, GDB finds the symbols for you automatically.

(At the Cauldron a couple months ago, several people told me that they
run into a similar issue when debugging other C++ projects.  One
example was when debugging LLVM, which puts all its code under the
"llvm" namespace.)

Implementation-wise, what the patch does is:

  - makes C++ symbol name hashing only consider the last component of
    a symbol name. (so that we can look up symbol names by
    last-component name only).

  - adds a C++ symbol name matcher for symbol_name_match_type::WILD,
    which ignores missing leading specifiers / components.

  - adjusts a few preexisting testsuite tests to use "-qualified" when
    they mean it.

  - adds new testsuite tests.

  - adds unit tests.

Grows the gdb.linespec/ tests like this:

  -# of expected passes           7823
  +# of expected passes           8977

gdb/ChangeLog:
2017-11-29  Pedro Alves  <palves@redhat.com>

	* NEWS: Mention that breakpoints on C++ functions are now set on
	on all namespaces/classes by default, and mention "break
	-qualified".
	* ax-gdb.c (agent_command_1): Adjust to pass a
	symbol_name_match_type to new_linespec_location.
	* breakpoint.c (parse_breakpoint_sals): Adjust to
	get_linespec_location's return type change.
	(strace_marker_create_sals_from_location): Adjust to pass a
	symbol_name_match_type to new_linespec_location.
	(strace_marker_decode_location): Adjust to get_linespec_location's
	return type change.
	(strace_command): Adjust to pass a symbol_name_match_type to
	new_linespec_location.
	(LOCATION_HELP_STRING): Add paragraph about wildmatching, and
	mention "-qualified".
	* c-lang.c (cplus_language_defn): Install cp_search_name_hash.
	* completer.c (explicit_location_match_type::MATCH_QUALIFIED): New
	enumerator.
	(complete_address_and_linespec_locations): New parameter
	'match_type'.  Pass it down.
	(explicit_options): Add "-qualified".
	(collect_explicit_location_matches): Pass the requested match type
	to the linespec completers.  Handle MATCH_QUALIFIED.
	(location_completer): Handle "-qualified" combined with linespecs.
	* cp-support.c (cp_search_name_hash): New.
	(cp_symbol_name_matches_1): Implement wild matching for C++.
	(cp_fq_symbol_name_matches): Reimplement.
	(cp_get_symbol_name_matcher): Return different matchers depending
	on the lookup name's match type.
	(selftests::test_cp_symbol_name_matches): Add wild matching tests.
	* cp-support.h (cp_search_name_hash): New declaration.
	* dwarf2read.c
	(selftests::dw2_expand_symtabs_matching::test_symbols): Add
	symbols.
	(test_dw2_expand_symtabs_matching_symbol): Add wild matching
	tests.
	* guile/scm-breakpoint.c (gdbscm_register_breakpoint_x): Adjust to
	pass a symbol_name_match_type to new_linespec_location.
	* linespec.c (linespec_parse_basic): Lookup function symbols using
	the parser's symbol name match type.
	(convert_explicit_location_to_linespec): New
	symbol_name_match_type parameter.  Pass it down to
	find_linespec_symbols.
	(convert_explicit_location_to_sals): Pass the location's name
	match type to convert_explicit_location_to_linespec.
	(parse_linespec): New match_type parameter.  Save it in the
	parser.
	(linespec_parser_new): Default to symbol_name_match_type::WILD.
	(linespec_complete_function): New symbol_name_match_type
	parameter.  Use it.
	(complete_linespec_component): Pass down the parser's recorded
	name match type.
	(linespec_complete_label): New symbol_name_match_type parameter.
	Use it.
	(linespec_complete): New symbol_name_match_type parameter.  Save
	it in the parser and pass it down.  Adjust to
	get_linespec_location's prototype change.
	(find_function_symbols, find_linespec_symbols): New
	symbol_name_match_type parameter.  Pass it down instead of
	assuming symbol_name_match_type::WILD.
	* linespec.h (linespec_complete, linespec_complete_function)
	(linespec_complete_label): New symbol_name_match_type parameter.
	* location.c (event_location::linespec_location): Now a struct
	linespec_location.
	(EL_LINESPEC): Adjust.
	(initialize_explicit_location): Default to
	symbol_name_match_type::WILD.
	(new_linespec_location): New symbol_name_match_type parameter.
	Record it in the location.
	(get_linespec_location): Now returns a struct linespec_location.
	(new_explicit_location): Also copy func_name_match_type.
	(explicit_to_string_internal)
	(string_to_explicit_location): Handle "-qualified".
	(copy_event_location): Adjust to LINESPEC_LOCATION type change.
	Copy symbol_name_match_type fields.
	(event_location_deleter::operator()): Adjust to LINESPEC_LOCATION
	type change.
	(event_location_to_string): Adjust to LINESPEC_LOCATION type
	change.  Handle "-qualfied".
	(string_to_explicit_location): Handle "-qualified".
	(string_to_event_location_basic): New symbol_name_match_type
	parameter.  Pass it down.
	(string_to_event_location): Handle "-qualified".
	* location.h (struct linespec_location): New.
	(explicit_location::func_name_match_type): New field.
	(new_linespec_location): Now returns a const linespec_location *.
	(string_to_event_location_basic): New symbol_name_match_type
	parameter.
	(explicit_completion_info::saw_explicit_location_option): New
	field.
	* mi/mi-cmd-break.c (mi_cmd_break_insert_1): Adjust to pass a
	symbol_name_match_type to new_linespec_location.
	* python/py-breakpoint.c (bppy_init): Likewise.
	* python/python.c (gdbpy_decode_line): Likewise.

gdb/testsuite/ChangeLog:
2017-11-29  Pedro Alves  <palves@redhat.com>

	* gdb.base/langs.exp: Use -qualified.
	* gdb.cp/meth-typedefs.exp: Use -qualified, and add tests without
	it.
	* gdb.cp/namespace.exp: Use -qualified.
	* gdb.linespec/cpcompletion.exp (overload-2, fqn, fqn-2)
	(overload-3, template-overload, template-ret-type, const-overload)
	(const-overload-quoted, anon-ns, ambiguous-prefix): New
	procedures.
	(test_driver): Call them.
	* gdb.cp/save-bp-qualified.cc: New.
	* gdb.cp/save-bp-qualified.exp: New.
	* gdb.linespec/explicit.exp: Test -qualified.
	* lib/completion-support.exp (completion::explicit_opts_list): Add
	"-qualified".
	* lib/gdb.exp (gdb_breakpoint): Handle "qualified".

gdb/doc/ChangeLog:
2017-11-29  Pedro Alves  <palves@redhat.com>

	* gdb.texinfo (Linespec Locations): Document how "function" is
	interpreted in C++ and Ada.  Document "-qualified".
	(Explicit Locations): Document how "-function" is interpreted in
	C++ and Ada.  Document "-qualified".
2017-11-29 19:43:48 +00:00

525 lines
12 KiB
C

/* MI Command Set - breakpoint and watchpoint commands.
Copyright (C) 2000-2017 Free Software Foundation, Inc.
Contributed by Cygnus Solutions (a Red Hat company).
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 "arch-utils.h"
#include "mi-cmds.h"
#include "ui-out.h"
#include "mi-out.h"
#include "breakpoint.h"
#include "mi-getopt.h"
#include "observer.h"
#include "mi-main.h"
#include "mi-cmd-break.h"
#include "language.h"
#include "location.h"
#include "linespec.h"
#include "gdb_obstack.h"
#include <ctype.h>
enum
{
FROM_TTY = 0
};
/* True if MI breakpoint observers have been registered. */
static int mi_breakpoint_observers_installed;
/* Control whether breakpoint_notify may act. */
static int mi_can_breakpoint_notify;
/* Output a single breakpoint, when allowed. */
static void
breakpoint_notify (struct breakpoint *b)
{
if (mi_can_breakpoint_notify)
{
TRY
{
print_breakpoint (b);
}
CATCH (ex, RETURN_MASK_ALL)
{
exception_print (gdb_stderr, ex);
}
END_CATCH
}
}
enum bp_type
{
REG_BP,
HW_BP,
REGEXP_BP
};
/* Arrange for all new breakpoints and catchpoints to be reported to
CURRENT_UIOUT until the destructor of the returned scoped_restore
is run.
Note that MI output will be probably invalid if more than one
breakpoint is created inside one MI command. */
scoped_restore_tmpl<int>
setup_breakpoint_reporting (void)
{
if (! mi_breakpoint_observers_installed)
{
observer_attach_breakpoint_created (breakpoint_notify);
mi_breakpoint_observers_installed = 1;
}
return make_scoped_restore (&mi_can_breakpoint_notify, 1);
}
/* Convert arguments in ARGV to the string in "format",argv,argv...
and return it. */
static std::string
mi_argv_to_format (char **argv, int argc)
{
int i;
std::string result;
/* Convert ARGV[OIND + 1] to format string and save to FORMAT. */
result += '\"';
for (i = 0; i < strlen (argv[0]); i++)
{
switch (argv[0][i])
{
case '\\':
result += "\\\\";
break;
case '\a':
result += "\\a";
break;
case '\b':
result += "\\b";
break;
case '\f':
result += "\\f";
break;
case '\n':
result += "\\n";
break;
case '\r':
result += "\\r";
break;
case '\t':
result += "\\t";
break;
case '\v':
result += "\\v";
break;
case '"':
result += "\\\"";
break;
default:
if (isprint (argv[0][i]))
result += argv[0][i];
else
{
char tmp[5];
xsnprintf (tmp, sizeof (tmp), "\\%o",
(unsigned char) argv[0][i]);
result += tmp;
}
break;
}
}
result += '\"';
/* Apply other argv to FORMAT. */
for (i = 1; i < argc; i++)
{
result += ',';
result += argv[i];
}
return result;
}
/* Insert breakpoint.
If dprintf is true, it will insert dprintf.
If not, it will insert other type breakpoint. */
static void
mi_cmd_break_insert_1 (int dprintf, const char *command, char **argv, int argc)
{
const char *address = NULL;
int hardware = 0;
int temp_p = 0;
int thread = -1;
int ignore_count = 0;
char *condition = NULL;
int pending = 0;
int enabled = 1;
int tracepoint = 0;
enum bptype type_wanted;
event_location_up location;
struct breakpoint_ops *ops;
int is_explicit = 0;
struct explicit_location explicit_loc;
std::string extra_string;
enum opt
{
HARDWARE_OPT, TEMP_OPT, CONDITION_OPT,
IGNORE_COUNT_OPT, THREAD_OPT, PENDING_OPT, DISABLE_OPT,
TRACEPOINT_OPT,
EXPLICIT_SOURCE_OPT, EXPLICIT_FUNC_OPT,
EXPLICIT_LABEL_OPT, EXPLICIT_LINE_OPT
};
static const struct mi_opt opts[] =
{
{"h", HARDWARE_OPT, 0},
{"t", TEMP_OPT, 0},
{"c", CONDITION_OPT, 1},
{"i", IGNORE_COUNT_OPT, 1},
{"p", THREAD_OPT, 1},
{"f", PENDING_OPT, 0},
{"d", DISABLE_OPT, 0},
{"a", TRACEPOINT_OPT, 0},
{"-source" , EXPLICIT_SOURCE_OPT, 1},
{"-function", EXPLICIT_FUNC_OPT, 1},
{"-label", EXPLICIT_LABEL_OPT, 1},
{"-line", EXPLICIT_LINE_OPT, 1},
{ 0, 0, 0 }
};
/* Parse arguments. It could be -r or -h or -t, <location> or ``--''
to denote the end of the option list. */
int oind = 0;
char *oarg;
initialize_explicit_location (&explicit_loc);
while (1)
{
int opt = mi_getopt ("-break-insert", argc, argv,
opts, &oind, &oarg);
if (opt < 0)
break;
switch ((enum opt) opt)
{
case TEMP_OPT:
temp_p = 1;
break;
case HARDWARE_OPT:
hardware = 1;
break;
case CONDITION_OPT:
condition = oarg;
break;
case IGNORE_COUNT_OPT:
ignore_count = atol (oarg);
break;
case THREAD_OPT:
thread = atol (oarg);
break;
case PENDING_OPT:
pending = 1;
break;
case DISABLE_OPT:
enabled = 0;
break;
case TRACEPOINT_OPT:
tracepoint = 1;
break;
case EXPLICIT_SOURCE_OPT:
is_explicit = 1;
explicit_loc.source_filename = oarg;
break;
case EXPLICIT_FUNC_OPT:
is_explicit = 1;
explicit_loc.function_name = oarg;
break;
case EXPLICIT_LABEL_OPT:
is_explicit = 1;
explicit_loc.label_name = oarg;
break;
case EXPLICIT_LINE_OPT:
is_explicit = 1;
explicit_loc.line_offset = linespec_parse_line_offset (oarg);
break;
}
}
if (oind >= argc && !is_explicit)
error (_("-%s-insert: Missing <location>"),
dprintf ? "dprintf" : "break");
if (dprintf)
{
int format_num = is_explicit ? oind : oind + 1;
if (hardware || tracepoint)
error (_("-dprintf-insert: does not support -h or -a"));
if (format_num >= argc)
error (_("-dprintf-insert: Missing <format>"));
extra_string = mi_argv_to_format (argv + format_num, argc - format_num);
address = argv[oind];
}
else
{
if (is_explicit)
{
if (oind < argc)
error (_("-break-insert: Garbage following explicit location"));
}
else
{
if (oind < argc - 1)
error (_("-break-insert: Garbage following <location>"));
address = argv[oind];
}
}
/* Now we have what we need, let's insert the breakpoint! */
scoped_restore restore_breakpoint_reporting = setup_breakpoint_reporting ();
if (tracepoint)
{
/* Note that to request a fast tracepoint, the client uses the
"hardware" flag, although there's nothing of hardware related to
fast tracepoints -- one can implement slow tracepoints with
hardware breakpoints, but fast tracepoints are always software.
"fast" is a misnomer, actually, "jump" would be more appropriate.
A simulator or an emulator could conceivably implement fast
regular non-jump based tracepoints. */
type_wanted = hardware ? bp_fast_tracepoint : bp_tracepoint;
ops = &tracepoint_breakpoint_ops;
}
else if (dprintf)
{
type_wanted = bp_dprintf;
ops = &dprintf_breakpoint_ops;
}
else
{
type_wanted = hardware ? bp_hardware_breakpoint : bp_breakpoint;
ops = &bkpt_breakpoint_ops;
}
if (is_explicit)
{
/* Error check -- we must have one of the other
parameters specified. */
if (explicit_loc.source_filename != NULL
&& explicit_loc.function_name == NULL
&& explicit_loc.label_name == NULL
&& explicit_loc.line_offset.sign == LINE_OFFSET_UNKNOWN)
error (_("-%s-insert: --source option requires --function, --label,"
" or --line"), dprintf ? "dprintf" : "break");
location = new_explicit_location (&explicit_loc);
}
else
{
location = string_to_event_location_basic (&address, current_language,
symbol_name_match_type::WILD);
if (*address)
error (_("Garbage '%s' at end of location"), address);
}
create_breakpoint (get_current_arch (), location.get (), condition, thread,
extra_string.c_str (),
0 /* condition and thread are valid. */,
temp_p, type_wanted,
ignore_count,
pending ? AUTO_BOOLEAN_TRUE : AUTO_BOOLEAN_FALSE,
ops, 0, enabled, 0, 0);
}
/* Implements the -break-insert command.
See the MI manual for the list of possible options. */
void
mi_cmd_break_insert (const char *command, char **argv, int argc)
{
mi_cmd_break_insert_1 (0, command, argv, argc);
}
/* Implements the -dprintf-insert command.
See the MI manual for the list of possible options. */
void
mi_cmd_dprintf_insert (const char *command, char **argv, int argc)
{
mi_cmd_break_insert_1 (1, command, argv, argc);
}
enum wp_type
{
REG_WP,
READ_WP,
ACCESS_WP
};
void
mi_cmd_break_passcount (const char *command, char **argv, int argc)
{
int n;
int p;
struct tracepoint *t;
if (argc != 2)
error (_("Usage: tracepoint-number passcount"));
n = atoi (argv[0]);
p = atoi (argv[1]);
t = get_tracepoint (n);
if (t)
{
t->pass_count = p;
observer_notify_breakpoint_modified (t);
}
else
{
error (_("Could not find tracepoint %d"), n);
}
}
/* Insert a watchpoint. The type of watchpoint is specified by the
first argument:
-break-watch <expr> --> insert a regular wp.
-break-watch -r <expr> --> insert a read watchpoint.
-break-watch -a <expr> --> insert an access wp. */
void
mi_cmd_break_watch (const char *command, char **argv, int argc)
{
char *expr = NULL;
enum wp_type type = REG_WP;
enum opt
{
READ_OPT, ACCESS_OPT
};
static const struct mi_opt opts[] =
{
{"r", READ_OPT, 0},
{"a", ACCESS_OPT, 0},
{ 0, 0, 0 }
};
/* Parse arguments. */
int oind = 0;
char *oarg;
while (1)
{
int opt = mi_getopt ("-break-watch", argc, argv,
opts, &oind, &oarg);
if (opt < 0)
break;
switch ((enum opt) opt)
{
case READ_OPT:
type = READ_WP;
break;
case ACCESS_OPT:
type = ACCESS_WP;
break;
}
}
if (oind >= argc)
error (_("-break-watch: Missing <expression>"));
if (oind < argc - 1)
error (_("-break-watch: Garbage following <expression>"));
expr = argv[oind];
/* Now we have what we need, let's insert the watchpoint! */
switch (type)
{
case REG_WP:
watch_command_wrapper (expr, FROM_TTY, 0);
break;
case READ_WP:
rwatch_command_wrapper (expr, FROM_TTY, 0);
break;
case ACCESS_WP:
awatch_command_wrapper (expr, FROM_TTY, 0);
break;
default:
error (_("-break-watch: Unknown watchpoint type."));
}
}
/* The mi_read_next_line consults these variable to return successive
command lines. While it would be clearer to use a closure pointer,
it is not expected that any future code will use read_command_lines_1,
therefore no point of overengineering. */
static char **mi_command_line_array;
static int mi_command_line_array_cnt;
static int mi_command_line_array_ptr;
static char *
mi_read_next_line (void)
{
if (mi_command_line_array_ptr == mi_command_line_array_cnt)
return NULL;
else
return mi_command_line_array[mi_command_line_array_ptr++];
}
void
mi_cmd_break_commands (const char *command, char **argv, int argc)
{
command_line_up break_command;
char *endptr;
int bnum;
struct breakpoint *b;
if (argc < 1)
error (_("USAGE: %s <BKPT> [<COMMAND> [<COMMAND>...]]"), command);
bnum = strtol (argv[0], &endptr, 0);
if (endptr == argv[0])
error (_("breakpoint number argument \"%s\" is not a number."),
argv[0]);
else if (*endptr != '\0')
error (_("junk at the end of breakpoint number argument \"%s\"."),
argv[0]);
b = get_breakpoint (bnum);
if (b == NULL)
error (_("breakpoint %d not found."), bnum);
mi_command_line_array = argv;
mi_command_line_array_ptr = 1;
mi_command_line_array_cnt = argc;
if (is_tracepoint (b))
break_command = read_command_lines_1 (mi_read_next_line, 1,
check_tracepoint_command, b);
else
break_command = read_command_lines_1 (mi_read_next_line, 1, 0, 0);
breakpoint_set_commands (b, std::move (break_command));
}