gdb/mi: New commands to catch C++ exceptions

Adds some MI commands to catch C++ exceptions.  The new commands are
-catch-throw, -catch-rethrow, and -catch-catch, these all correspond
to the CLI commands 'catch throw', 'catch rethrow', and 'catch catch'.

Each MI command takes two optional arguments, '-t' has the effect of
calling 'tcatch' instead of 'catch', for example:

   (gdb)
   -catch-throw -t

Is the same as:

   (gdb) tcatch throw

There is also a '-r REGEXP' argument that can supply a regexp to match
against the exception type, so:

   (gdb)
   -catch-catch -r PATTERN

Is the same as:

   (gdb) catch catch PATTERN

The change in print_mention_exception_catchpoint might seem a little
strange; changing the output from using ui_out::field_int and
ui_out::text to using  ui_out::message.

The print_mention_exception_catchpoint is used as the 'print_mention'
method for the exception catchpoint breakpoint object.  Most of the
other 'print_mention' methods (see breakpoint.c) use either
printf_filtered, of ui_out::message.  Using field_int was causing an
unexpected field to be added to the MI output.  Here's the output
without the change in print_mention_exception_catchpoint:

    (gdb)
    -catch-throw
    ^done,bkptno="1",bkpt={number="1",type="breakpoint",disp="keep",
                           enabled="y",addr="0x00000000004006c0",
                           what="exception throw",catch-type="throw",
                           thread-groups=["i1"],times="0"}

Notice the breakpoint number appears in both the 'bkptno' field, and
the 'number' field within the 'bkpt' tuple.  Here's the output with
the change in print_mention_exception_catchpoint:

    (gdb)
    -catch-throw
    ^done,bkpt={number="1",type="breakpoint",disp="keep",
                enabled="y",addr="0x00000000004006c0",
                what="exception throw",catch-type="throw",
                thread-groups=["i1"],times="0"}

gdb/ChangeLog:

	* NEWS: Mention new MI commands.
	* break-catch-throw.c (enum exception_event_kind): Move to
	breakpoint.h.
	(print_mention_exception_catchpoint): Output text as a single
	message.
	(catch_exception_command_1): Rename to...
	(catch_exception_event): ...this, make non-static, update header
	command, and change some parameter types.
	(catch_catch_command): Update for changes to
	catch_exception_command_1.
	(catch_throw_command): Likewise.
	(catch_rethrow_command): Likewise.
	* breakpoint.c (enum exception_event_kind): Delete.
	* breakpoint.h (enum exception_event_kind): Moved here from
	break-catch-throw.c.
	(catch_exception_event): Declare.
	* mi/mi-cmd-catch.c (mi_cmd_catch_exception_event): New function.
	(mi_cmd_catch_throw): New function.
	(mi_cmd_catch_rethrow): New function.
	(mi_cmd_catch_catch): New function.
	* mi/mi-cmds.c (mi_cmds): Add 'catch-throw', 'catch-rethrow', and
	'catch-catch' entries.
	* mi/mi-cmds.h (mi_cmd_catch_throw): Declare.
	(mi_cmd_catch_rethrow): Declare.
	(mi_cmd_catch_catch): Declare.

gdb/doc/ChangeLog:

	* gdb.texinfo (GDB/MI Catchpoint Commands): Add menu entry to new
	node.
	(C++ Exception GDB/MI Catchpoint Commands): New node to describe
	new MI commands.

gdb/testsuite/ChangeLog:

	* gdb.mi/mi-catch-cpp-exceptions.cc: New file.
	* gdb.mi/mi-catch-cpp-exceptions.exp: New file.
	* lib/mi-support.exp (mi_expect_stop): Handle 'exception-caught'
	as a stop reason.
This commit is contained in:
Andrew Burgess
2019-05-08 19:01:36 +01:00
parent ec8e2b6d30
commit 30056ea04a
14 changed files with 580 additions and 37 deletions

View File

@ -1,3 +1,31 @@
2019-06-15 Andrew Burgess <andrew.burgess@embecosm.com>
* NEWS: Mention new MI commands.
* break-catch-throw.c (enum exception_event_kind): Move to
breakpoint.h.
(print_mention_exception_catchpoint): Output text as a single
message.
(catch_exception_command_1): Rename to...
(catch_exception_event): ...this, make non-static, update header
command, and change some parameter types.
(catch_catch_command): Update for changes to
catch_exception_command_1.
(catch_throw_command): Likewise.
(catch_rethrow_command): Likewise.
* breakpoint.c (enum exception_event_kind): Delete.
* breakpoint.h (enum exception_event_kind): Moved here from
break-catch-throw.c.
(catch_exception_event): Declare.
* mi/mi-cmd-catch.c (mi_cmd_catch_exception_event): New function.
(mi_cmd_catch_throw): New function.
(mi_cmd_catch_rethrow): New function.
(mi_cmd_catch_catch): New function.
* mi/mi-cmds.c (mi_cmds): Add 'catch-throw', 'catch-rethrow', and
'catch-catch' entries.
* mi/mi-cmds.h (mi_cmd_catch_throw): Declare.
(mi_cmd_catch_rethrow): Declare.
(mi_cmd_catch_catch): Declare.
2019-06-15 Andrew Burgess <andrew.burgess@embecosm.com> 2019-06-15 Andrew Burgess <andrew.burgess@embecosm.com>
* annotate.c (annotate_source_line): Change return type to void, * annotate.c (annotate_source_line): Change return type to void,

View File

@ -213,6 +213,10 @@ maint show test-options-completion-result
were to be given as a command itself. This is intended for use by MI were to be given as a command itself. This is intended for use by MI
frontends in cases when separate CLI and MI channels cannot be used. frontends in cases when separate CLI and MI channels cannot be used.
-catch-throw, -catch-rethrow, and -catch-catch
These can be used to catch C++ exceptions in a similar fashion to
the CLI commands 'catch throw', 'catch rethrow', and 'catch catch'.
* Testsuite * Testsuite
The testsuite now creates the files gdb.cmd (containing the arguments The testsuite now creates the files gdb.cmd (containing the arguments

View File

@ -37,14 +37,6 @@
#include "cp-support.h" #include "cp-support.h"
#include "location.h" #include "location.h"
/* Enums for exception-handling support. */
enum exception_event_kind
{
EX_EVENT_THROW,
EX_EVENT_RETHROW,
EX_EVENT_CATCH
};
/* Each spot where we may place an exception-related catchpoint has /* Each spot where we may place an exception-related catchpoint has
two names: the SDT probe point and the function name. This two names: the SDT probe point and the function name. This
structure holds both. */ structure holds both. */
@ -317,12 +309,12 @@ print_mention_exception_catchpoint (struct breakpoint *b)
enum exception_event_kind kind = classify_exception_breakpoint (b); enum exception_event_kind kind = classify_exception_breakpoint (b);
bp_temp = b->disposition == disp_del; bp_temp = b->disposition == disp_del;
uiout->text (bp_temp ? _("Temporary catchpoint ") uiout->message ("%s %d %s",
: _("Catchpoint ")); (bp_temp ? _("Temporary catchpoint ") : _("Catchpoint")),
uiout->field_int ("bkptno", b->number); b->number,
uiout->text ((kind == EX_EVENT_THROW ? _(" (throw)") (kind == EX_EVENT_THROW
: (kind == EX_EVENT_CATCH ? _(" (catch)") ? _("(throw)") : (kind == EX_EVENT_CATCH
: _(" (rethrow)")))); ? _("(catch)") : _("(rethrow)"))));
} }
/* Implement the "print_recreate" breakpoint_ops method for throw and /* Implement the "print_recreate" breakpoint_ops method for throw and
@ -420,13 +412,11 @@ extract_exception_regexp (const char **string)
return std::string (); return std::string ();
} }
/* Deal with "catch catch", "catch throw", and "catch rethrow" /* See breakpoint.h. */
commands. */
static void void
catch_exception_command_1 (enum exception_event_kind ex_event, catch_exception_event (enum exception_event_kind ex_event,
const char *arg, const char *arg, bool tempflag, int from_tty)
int tempflag, int from_tty)
{ {
const char *cond_string = NULL; const char *cond_string = NULL;
@ -456,9 +446,9 @@ static void
catch_catch_command (const char *arg, int from_tty, catch_catch_command (const char *arg, int from_tty,
struct cmd_list_element *command) struct cmd_list_element *command)
{ {
int tempflag = get_cmd_context (command) == CATCH_TEMPORARY; bool tempflag = get_cmd_context (command) == CATCH_TEMPORARY;
catch_exception_command_1 (EX_EVENT_CATCH, arg, tempflag, from_tty); catch_exception_event (EX_EVENT_CATCH, arg, tempflag, from_tty);
} }
/* Implementation of "catch throw" command. */ /* Implementation of "catch throw" command. */
@ -467,9 +457,9 @@ static void
catch_throw_command (const char *arg, int from_tty, catch_throw_command (const char *arg, int from_tty,
struct cmd_list_element *command) struct cmd_list_element *command)
{ {
int tempflag = get_cmd_context (command) == CATCH_TEMPORARY; bool tempflag = get_cmd_context (command) == CATCH_TEMPORARY;
catch_exception_command_1 (EX_EVENT_THROW, arg, tempflag, from_tty); catch_exception_event (EX_EVENT_THROW, arg, tempflag, from_tty);
} }
/* Implementation of "catch rethrow" command. */ /* Implementation of "catch rethrow" command. */
@ -478,9 +468,9 @@ static void
catch_rethrow_command (const char *arg, int from_tty, catch_rethrow_command (const char *arg, int from_tty,
struct cmd_list_element *command) struct cmd_list_element *command)
{ {
int tempflag = get_cmd_context (command) == CATCH_TEMPORARY; bool tempflag = get_cmd_context (command) == CATCH_TEMPORARY;
catch_exception_command_1 (EX_EVENT_RETHROW, arg, tempflag, from_tty); catch_exception_event (EX_EVENT_RETHROW, arg, tempflag, from_tty);
} }

View File

@ -85,14 +85,6 @@
#include "common/array-view.h" #include "common/array-view.h"
#include "common/gdb_optional.h" #include "common/gdb_optional.h"
/* Enums for exception-handling support. */
enum exception_event_kind
{
EX_EVENT_THROW,
EX_EVENT_RETHROW,
EX_EVENT_CATCH
};
/* Prototypes for local functions. */ /* Prototypes for local functions. */
static void map_breakpoint_numbers (const char *, static void map_breakpoint_numbers (const char *,

View File

@ -42,6 +42,16 @@ struct linespec_result;
struct linespec_sals; struct linespec_sals;
struct inferior; struct inferior;
/* Enum for exception-handling support in 'catch throw', 'catch rethrow',
'catch catch' and the MI equivalent. */
enum exception_event_kind
{
EX_EVENT_THROW,
EX_EVENT_RETHROW,
EX_EVENT_CATCH
};
/* Why are we removing the breakpoint from the target? */ /* Why are we removing the breakpoint from the target? */
enum remove_bp_reason enum remove_bp_reason
@ -1675,4 +1685,14 @@ extern cmd_list_element *commands_cmd_element;
extern bool fix_multi_location_breakpoint_output_globally; extern bool fix_multi_location_breakpoint_output_globally;
/* Deal with "catch catch", "catch throw", and "catch rethrow" commands and
the MI equivalents. Sets up to catch events of type EX_EVENT. When
TEMPFLAG is true only the next matching event is caught after which the
catch-point is deleted. If REGEX is not NULL then only exceptions whose
type name matches REGEX will trigger the event. */
extern void catch_exception_event (enum exception_event_kind ex_event,
const char *regex, bool tempflag,
int from_tty);
#endif /* !defined (BREAKPOINT_H) */ #endif /* !defined (BREAKPOINT_H) */

View File

@ -1,3 +1,10 @@
2019-06-15 Andrew Burgess <andrew.burgess@embecosm.com>
* gdb.texinfo (GDB/MI Catchpoint Commands): Add menu entry to new
node.
(C++ Exception GDB/MI Catchpoint Commands): New node to describe
new MI commands.
2019-06-13 Pedro Alves <palves@redhat.com> 2019-06-13 Pedro Alves <palves@redhat.com>
* gdb.texinfo (Command Completion): Mention command options too. * gdb.texinfo (Command Completion): Mention command options too.

View File

@ -29793,6 +29793,7 @@ catchpoints.
@menu @menu
* Shared Library GDB/MI Catchpoint Commands:: * Shared Library GDB/MI Catchpoint Commands::
* Ada Exception GDB/MI Catchpoint Commands:: * Ada Exception GDB/MI Catchpoint Commands::
* C++ Exception GDB/MI Catchpoint Commands::
@end menu @end menu
@node Shared Library GDB/MI Catchpoint Commands @node Shared Library GDB/MI Catchpoint Commands
@ -29992,6 +29993,145 @@ times="0",original-location="__gnat_begin_handler"@}
(gdb) (gdb)
@end smallexample @end smallexample
@node C++ Exception GDB/MI Catchpoint Commands
@subsection C@t{++} Exception @sc{gdb/mi} Catchpoints
The following @sc{gdb/mi} commands can be used to create catchpoints
that stop the execution when C@t{++} exceptions are being throw, rethrown,
or caught.
@subheading The @code{-catch-throw} Command
@findex -catch-throw
@subsubheading Synopsis
@smallexample
-catch-throw [ -t ] [ -r @var{regexp}]
@end smallexample
Stop when the debuggee throws a C@t{++} exception. If @var{regexp} is
given, then only exceptions whose type matches the regular expression
will be caught.
If @samp{-t} is given, then the catchpoint is enabled only for one
stop, the catchpoint is automatically deleted after stopping once for
the event.
@subsubheading @value{GDBN} Command
The corresponding @value{GDBN} commands are @samp{catch throw}
and @samp{tcatch throw} (@pxref{Set Catchpoints}).
@subsubheading Example
@smallexample
-catch-throw -r exception_type
^done,bkpt=@{number="1",type="breakpoint",disp="keep",enabled="y",
addr="0x00000000004006c0",what="exception throw",
catch-type="throw",thread-groups=["i1"],
regexp="exception_type",times="0"@}
(gdb)
-exec-run
^running
(gdb)
~"\n"
~"Catchpoint 1 (exception thrown), 0x00007ffff7ae00ed
in __cxa_throw () from /lib64/libstdc++.so.6\n"
*stopped,bkptno="1",reason="breakpoint-hit",disp="keep",
frame=@{addr="0x00007ffff7ae00ed",func="__cxa_throw",
args=[],from="/lib64/libstdc++.so.6",arch="i386:x86-64"@},
thread-id="1",stopped-threads="all",core="6"
(gdb)
@end smallexample
@subheading The @code{-catch-rethrow} Command
@findex -catch-rethrow
@subsubheading Synopsis
@smallexample
-catch-rethrow [ -t ] [ -r @var{regexp}]
@end smallexample
Stop when a C@t{++} exception is re-thrown. If @var{regexp} is given,
then only exceptions whose type matches the regular expression will be
caught.
If @samp{-t} is given, then the catchpoint is enabled only for one
stop, the catchpoint is automatically deleted after the first event is
caught.
@subsubheading @value{GDBN} Command
The corresponding @value{GDBN} commands are @samp{catch rethrow}
and @samp{tcatch rethrow} (@pxref{Set Catchpoints}).
@subsubheading Example
@smallexample
-catch-rethrow -r exception_type
^done,bkpt=@{number="1",type="breakpoint",disp="keep",enabled="y",
addr="0x00000000004006c0",what="exception rethrow",
catch-type="rethrow",thread-groups=["i1"],
regexp="exception_type",times="0"@}
(gdb)
-exec-run
^running
(gdb)
~"\n"
~"Catchpoint 1 (exception rethrown), 0x00007ffff7ae00ed
in __cxa_rethrow () from /lib64/libstdc++.so.6\n"
*stopped,bkptno="1",reason="breakpoint-hit",disp="keep",
frame=@{addr="0x00007ffff7ae00ed",func="__cxa_rethrow",
args=[],from="/lib64/libstdc++.so.6",arch="i386:x86-64"@},
thread-id="1",stopped-threads="all",core="6"
(gdb)
@end smallexample
@subheading The @code{-catch-catch} Command
@findex -catch-catch
@subsubheading Synopsis
@smallexample
-catch-catch [ -t ] [ -r @var{regexp}]
@end smallexample
Stop when the debuggee catches a C@t{++} exception. If @var{regexp}
is given, then only exceptions whose type matches the regular
expression will be caught.
If @samp{-t} is given, then the catchpoint is enabled only for one
stop, the catchpoint is automatically deleted after the first event is
caught.
@subsubheading @value{GDBN} Command
The corresponding @value{GDBN} commands are @samp{catch catch}
and @samp{tcatch catch} (@pxref{Set Catchpoints}).
@subsubheading Example
@smallexample
-catch-catch -r exception_type
^done,bkpt=@{number="1",type="breakpoint",disp="keep",enabled="y",
addr="0x00000000004006c0",what="exception catch",
catch-type="catch",thread-groups=["i1"],
regexp="exception_type",times="0"@}
(gdb)
-exec-run
^running
(gdb)
~"\n"
~"Catchpoint 1 (exception caught), 0x00007ffff7ae00ed
in __cxa_begin_catch () from /lib64/libstdc++.so.6\n"
*stopped,bkptno="1",reason="breakpoint-hit",disp="keep",
frame=@{addr="0x00007ffff7ae00ed",func="__cxa_begin_catch",
args=[],from="/lib64/libstdc++.so.6",arch="i386:x86-64"@},
thread-id="1",stopped-threads="all",core="6"
(gdb)
@end smallexample
@c %%%%%%%%%%%%%%%%%%%%%%%%%%%% SECTION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @c %%%%%%%%%%%%%%%%%%%%%%%%%%%% SECTION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@node GDB/MI Program Context @node GDB/MI Program Context
@section @sc{gdb/mi} Program Context @section @sc{gdb/mi} Program Context

View File

@ -288,3 +288,74 @@ mi_cmd_catch_unload (const char *cmd, char *argv[], int argc)
mi_catch_load_unload (0, argv, argc); mi_catch_load_unload (0, argv, argc);
} }
/* Core handler for -catch-throw, -catch-rethrow, and -catch-catch
commands. The argument handling for all of these is identical, we just
pass KIND through to GDB's core to select the correct event type. */
static void
mi_cmd_catch_exception_event (enum exception_event_kind kind,
const char *cmd, char *argv[], int argc)
{
char *regex = NULL;
bool temp = false;
int oind = 0;
char *oarg;
enum opt
{
OPT_TEMP,
OPT_REGEX,
};
static const struct mi_opt opts[] =
{
{ "t", OPT_TEMP, 0 },
{ "r", OPT_REGEX, 1 },
{ 0, 0, 0 }
};
for (;;)
{
int opt = mi_getopt (cmd, argc, argv, opts,
&oind, &oarg);
if (opt < 0)
break;
switch ((enum opt) opt)
{
case OPT_TEMP:
temp = true;
break;
case OPT_REGEX:
regex = oarg;
break;
}
}
scoped_restore restore_breakpoint_reporting = setup_breakpoint_reporting ();
catch_exception_event (kind, regex, temp, 0 /* from_tty */);
}
/* Handler for -catch-throw. */
void
mi_cmd_catch_throw (const char *cmd, char *argv[], int argc)
{
mi_cmd_catch_exception_event (EX_EVENT_THROW, cmd, argv, argc);
}
/* Handler for -catch-rethrow. */
void
mi_cmd_catch_rethrow (const char *cmd, char *argv[], int argc)
{
mi_cmd_catch_exception_event (EX_EVENT_RETHROW, cmd, argv, argc);
}
/* Handler for -catch-catch. */
void
mi_cmd_catch_catch (const char *cmd, char *argv[], int argc)
{
mi_cmd_catch_exception_event (EX_EVENT_CATCH, cmd, argv, argc);
}

View File

@ -75,6 +75,12 @@ static struct mi_cmd mi_cmds[] =
&mi_suppress_notification.breakpoint), &mi_suppress_notification.breakpoint),
DEF_MI_CMD_MI_1 ("catch-unload", mi_cmd_catch_unload, DEF_MI_CMD_MI_1 ("catch-unload", mi_cmd_catch_unload,
&mi_suppress_notification.breakpoint), &mi_suppress_notification.breakpoint),
DEF_MI_CMD_MI_1 ("catch-throw", mi_cmd_catch_throw,
&mi_suppress_notification.breakpoint),
DEF_MI_CMD_MI_1 ("catch-rethrow", mi_cmd_catch_rethrow,
&mi_suppress_notification.breakpoint),
DEF_MI_CMD_MI_1 ("catch-catch", mi_cmd_catch_catch,
&mi_suppress_notification.breakpoint),
DEF_MI_CMD_MI ("complete", mi_cmd_complete), DEF_MI_CMD_MI ("complete", mi_cmd_complete),
DEF_MI_CMD_MI ("data-disassemble", mi_cmd_disassemble), DEF_MI_CMD_MI ("data-disassemble", mi_cmd_disassemble),
DEF_MI_CMD_MI ("data-evaluate-expression", mi_cmd_data_evaluate_expression), DEF_MI_CMD_MI ("data-evaluate-expression", mi_cmd_data_evaluate_expression),

View File

@ -44,6 +44,9 @@ extern mi_cmd_argv_ftype mi_cmd_catch_exception;
extern mi_cmd_argv_ftype mi_cmd_catch_handlers; extern mi_cmd_argv_ftype mi_cmd_catch_handlers;
extern mi_cmd_argv_ftype mi_cmd_catch_load; extern mi_cmd_argv_ftype mi_cmd_catch_load;
extern mi_cmd_argv_ftype mi_cmd_catch_unload; extern mi_cmd_argv_ftype mi_cmd_catch_unload;
extern mi_cmd_argv_ftype mi_cmd_catch_throw;
extern mi_cmd_argv_ftype mi_cmd_catch_rethrow;
extern mi_cmd_argv_ftype mi_cmd_catch_catch;
extern mi_cmd_argv_ftype mi_cmd_disassemble; extern mi_cmd_argv_ftype mi_cmd_disassemble;
extern mi_cmd_argv_ftype mi_cmd_data_evaluate_expression; extern mi_cmd_argv_ftype mi_cmd_data_evaluate_expression;
extern mi_cmd_argv_ftype mi_cmd_data_list_register_names; extern mi_cmd_argv_ftype mi_cmd_data_list_register_names;

View File

@ -1,3 +1,10 @@
2019-06-15 Andrew Burgess <andrew.burgess@embecosm.com>
* gdb.mi/mi-catch-cpp-exceptions.cc: New file.
* gdb.mi/mi-catch-cpp-exceptions.exp: New file.
* lib/mi-support.exp (mi_expect_stop): Handle 'exception-caught'
as a stop reason.
2019-06-15 Andrew Burgess <andrew.burgess@embecosm.com> 2019-06-15 Andrew Burgess <andrew.burgess@embecosm.com>
* gdb.base/annota1.exp: Update expected results. * gdb.base/annota1.exp: Update expected results.

View File

@ -0,0 +1,73 @@
/* Copyright 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/>. */
class my_exception
{
private:
int m_value;
public:
my_exception (int v)
: m_value (v)
{
/* Nothing. */
}
};
void
bar ()
{
my_exception ex (4);
throw ex; /* Throw 1. */
}
void
foo ()
{
for (int i = 0; i < 2; ++i)
{
try
{
bar ();
}
catch (const my_exception &ex) /* Catch 1. */
{
if (i == 1)
throw; /* Throw 2. */
}
}
}
int
main ()
{
for (int i = 0; i < 2; ++i)
{
try
{
foo ();
}
catch (const my_exception &ex) /* Catch 2. */
{
if (i == 1)
return 1; /* Stop here. */
}
}
return 0;
}

View File

@ -0,0 +1,197 @@
# Copyright 2019 Free Software Foundation, Inc.
# 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/>.
# Test the -catch-throw, -catch-rethrow, and -catch-catch MI commands.
if { [skip_cplus_tests] } { continue }
load_lib mi-support.exp
set MIFLAGS "-i=mi"
standard_testfile .cc
if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug c++}] != "" } {
untested "failed to compile"
return -1
}
# Grab some line numbers we'll need.
set catch_1_lineno [gdb_get_line_number "Catch 1"]
set catch_2_lineno [gdb_get_line_number "Catch 2"]
set throw_1_lineno [gdb_get_line_number "Throw 1"]
set throw_2_lineno [gdb_get_line_number "Throw 2"]
set main_lineno [gdb_get_line_number "Stop here"]
# Restart this test, load the test binary and set a breakpoint in
# main.
proc restart_for_test {} {
global srcdir subdir binfile srcfile
global main_lineno
if {[mi_gdb_start]} {
continue
}
mi_delete_breakpoints
mi_gdb_reinitialize_dir $srcdir/$subdir
mi_gdb_load ${binfile}
mi_runto main
mi_create_breakpoint \
"$srcfile:${main_lineno}" "break before exiting program" \
-disp keep -func "main.*" \
-file ".*mi-catch-cpp-exceptions.cc" -line ${main_lineno}
}
# Issue an -exec-continue then wait for GDB to catch a C++ exception
# event in FUNC on LINE. Use TESTNAME to make tests unique.
proc continue_to_next_exception { func line testname } {
global hex
mi_send_resuming_command "exec-continue" \
"exec-continue"
mi_expect_stop "exception-caught" ".*" ".*" ".*" ".*" \
{} "run until an exception is caught: $testname"
mi_gdb_test "-stack-list-frames 1 1" \
"\\^done,stack=\\\[frame=\{level=\"1\",addr=\"$hex\",func=\"${func}\",.*,line=\"${line}\".*\}\\\]" \
"check previous frame: $testname"
}
# Issue an -exec-continue and stop at the breakpoint in main.
proc continue_to_breakpoint_in_main {} {
global main_lineno
mi_send_resuming_command "exec-continue" "exec-continue to main"
mi_expect_stop "breakpoint-hit" "main" ".*" ".*" "${main_lineno}" \
{.* disp="keep"} "run until breakpoint in main"
}
# TYPE is one of throw, rethrow, or catch. This proc creates a catch
# point using -catch-TYPE. The optional string EXTRA is any extra
# arguments to pass when setting up the catchpoint.
proc setup_catchpoint {type {extra ""}} {
global decimal
mi_gdb_test "-catch-${type} ${extra}" \
"\\^done,bkpt=\{number=\"$decimal\".*what=\"exception ${type}\",catch-type=\"${type}\".*\}" \
"Setup -catch-${type}"
}
# Ensure that -catch-throw will catch only throws and nothing else.
with_test_prefix "-catch-throw" {
restart_for_test
setup_catchpoint "throw"
continue_to_next_exception "bar" "${throw_1_lineno}" "throw 1"
continue_to_next_exception "bar" "${throw_1_lineno}" "throw 2"
continue_to_next_exception "bar" "${throw_1_lineno}" "throw 3"
continue_to_next_exception "bar" "${throw_1_lineno}" "throw 4"
continue_to_breakpoint_in_main
}
# Ensure that -catch-rethrow catches only rethrows and nothing else.
with_test_prefix "-catch-rethrow" {
restart_for_test
setup_catchpoint "rethrow"
continue_to_next_exception "foo" "${throw_2_lineno}" "rethrow 1"
continue_to_next_exception "foo" "${throw_2_lineno}" "rethrow 2"
continue_to_breakpoint_in_main
}
# Ensure that -catch-catch catches only catch points, and nothing
# else.
with_test_prefix "-catch-catch" {
restart_for_test
setup_catchpoint "catch"
continue_to_next_exception "foo" "${catch_1_lineno}" "catch 1"
continue_to_next_exception "foo" "${catch_1_lineno}" "catch 2"
continue_to_next_exception "main" "${catch_2_lineno}" "catch 3"
continue_to_next_exception "foo" "${catch_1_lineno}" "catch 4"
continue_to_next_exception "foo" "${catch_1_lineno}" "catch 5"
continue_to_next_exception "main" "${catch_2_lineno}" "catch 6"
continue_to_breakpoint_in_main
}
# Now check that all of the command with a regexp that doesn't match,
# don't trigger.
with_test_prefix "all with invalid regexp" {
restart_for_test
setup_catchpoint "throw" "-r blahblah"
setup_catchpoint "rethrow" "-r woofwoof"
setup_catchpoint "catch" "-r miowmiow"
# Would like to use 'continue_to_breakpoint_in_main' here, if
# there wasn't a bug that requires a use of kfail.
mi_send_resuming_command "exec-continue" \
"exec-continue"
set testname "run until breakpoint in main"
gdb_expect {
-re "could not find minimal symbol for typeinfo address.*$mi_gdb_prompt$" {
kfail "gdb/24541" "${testname}"
}
-re "\\*stopped,reason=\"breakpoint-hit\".*func=\"main\".*line=\"${main_lineno}\".*$mi_gdb_prompt$" {
pass "${testname}"
}
timeout {
fail "${testname} (timeout)"
}
}
}
# Now check that all of the commands with a regexp that does match,
# still trigger.
with_test_prefix "all with valid regexp" {
restart_for_test
setup_catchpoint "throw" "-r my_ex"
setup_catchpoint "rethrow" "-r _except"
setup_catchpoint "catch" "-r my_exception"
continue_to_next_exception "bar" "${throw_1_lineno}" "throw 1"
continue_to_next_exception "foo" "${catch_1_lineno}" "catch 1"
continue_to_next_exception "bar" "${throw_1_lineno}" "throw 2"
continue_to_next_exception "foo" "${catch_1_lineno}" "catch 2"
continue_to_next_exception "foo" "${throw_2_lineno}" "rethrow 1"
continue_to_next_exception "main" "${catch_2_lineno}" "catch 3"
continue_to_next_exception "bar" "${throw_1_lineno}" "throw 3"
continue_to_next_exception "foo" "${catch_1_lineno}" "catch 4"
continue_to_next_exception "bar" "${throw_1_lineno}" "throw 4"
continue_to_next_exception "foo" "${catch_1_lineno}" "catch 5"
continue_to_next_exception "foo" "${throw_2_lineno}" "rethrow 2"
continue_to_next_exception "main" "${catch_2_lineno}" "catch 6"
continue_to_breakpoint_in_main
}
# Check that the temporary switch works on its own.
with_test_prefix "all with -t" {
restart_for_test
setup_catchpoint "throw" "-t"
setup_catchpoint "rethrow" "-t"
setup_catchpoint "catch" "-t"
continue_to_next_exception "bar" "${throw_1_lineno}" "throw 1"
continue_to_next_exception "foo" "${catch_1_lineno}" "catch 1"
continue_to_next_exception "foo" "${throw_2_lineno}" "rethrow 1"
continue_to_breakpoint_in_main
}
# Check that the temporary switch works when used with a regexp.
restart_for_test
with_test_prefix "all with -t and regexp" {
setup_catchpoint "throw" "-t -r my_ex"
setup_catchpoint "rethrow" "-t -r _except"
setup_catchpoint "catch" "-t -r my_exception"
continue_to_next_exception "bar" "${throw_1_lineno}" "throw 1"
continue_to_next_exception "foo" "${catch_1_lineno}" "catch 1"
continue_to_next_exception "foo" "${throw_2_lineno}" "rethrow 1"
continue_to_breakpoint_in_main
}

View File

@ -1221,10 +1221,15 @@ proc mi_expect_stop { reason func args file line extra test } {
set args "\\\[$args\\\]" set args "\\\[$args\\\]"
set bn "" set bn ""
set ebn ""
if { $reason == "breakpoint-hit" } { if { $reason == "breakpoint-hit" } {
set bn {bkptno="[0-9]+",} set bn {bkptno="[0-9]+",}
} elseif { $reason == "solib-event" } { } elseif { $reason == "solib-event" } {
set bn ".*" set bn ".*"
} elseif { $reason == "exception-caught" } {
set ebn {bkptno="[0-9]+",}
set bn ".*"
set reason "breakpoint-hit"
} }
set r "" set r ""
@ -1235,9 +1240,9 @@ proc mi_expect_stop { reason func args file line extra test } {
set a $after_reason set a $after_reason
verbose -log "mi_expect_stop: expecting: \\*stopped,${r}${a}${bn}frame=\{addr=\"$hex\",func=\"$func\",args=$args,(?:file=\"$any$file\",fullname=\"${fullname_syntax}$file\",line=\"$line\",arch=\"$any\"|from=\"$file\")\}$after_stopped,thread-id=\"$decimal\",stopped-threads=$any\r\n($thread_selected_re|$breakpoint_re)*$prompt_re" verbose -log "mi_expect_stop: expecting: \\*stopped,${ebn}${r}${a}${bn}frame=\{addr=\"$hex\",func=\"$func\",args=$args,(?:file=\"$any$file\",fullname=\"${fullname_syntax}$file\",line=\"$line\",arch=\"$any\"|from=\"$file\")\}$after_stopped,thread-id=\"$decimal\",stopped-threads=$any\r\n($thread_selected_re|$breakpoint_re)*$prompt_re"
gdb_expect { gdb_expect {
-re "\\*stopped,${r}${a}${bn}frame=\{addr=\"$hex\",func=\"$func\",args=$args,(?:file=\"$any$file\",fullname=\"${fullname_syntax}$file\",line=\"($line)\",arch=\"$any\"|from=\"$file\")\}$after_stopped,thread-id=\"$decimal\",stopped-threads=$any\r\n($thread_selected_re|$breakpoint_re)*$prompt_re" { -re "\\*stopped,${ebn}${r}${a}${bn}frame=\{addr=\"$hex\",func=\"$func\",args=$args,(?:file=\"$any$file\",fullname=\"${fullname_syntax}$file\",line=\"($line)\",arch=\"$any\"|from=\"$file\")\}$after_stopped,thread-id=\"$decimal\",stopped-threads=$any\r\n($thread_selected_re|$breakpoint_re)*$prompt_re" {
pass "$test" pass "$test"
if {[array names expect_out "2,string"] != ""} { if {[array names expect_out "2,string"] != ""} {
return $expect_out(2,string) return $expect_out(2,string)
@ -1245,7 +1250,7 @@ proc mi_expect_stop { reason func args file line extra test } {
# No debug info available but $file does match. # No debug info available but $file does match.
return 0 return 0
} }
-re "\\*stopped,${r}${a}${bn}frame=\{addr=\"$hex\",func=\"$any\",args=\[\\\[\{\]$any\[\\\]\}\],file=\"$any\",fullname=\"${fullname_syntax}$any\",line=\"\[0-9\]*\",arch=\"$any\"\}$after_stopped,thread-id=\"$decimal\",stopped-threads=$any\r\n($thread_selected_re|$breakpoint_re)*$prompt_re" { -re "\\*stopped,${ebn}${r}${a}${bn}frame=\{addr=\"$hex\",func=\"$any\",args=\[\\\[\{\]$any\[\\\]\}\],file=\"$any\",fullname=\"${fullname_syntax}$any\",line=\"\[0-9\]*\",arch=\"$any\"\}$after_stopped,thread-id=\"$decimal\",stopped-threads=$any\r\n($thread_selected_re|$breakpoint_re)*$prompt_re" {
verbose -log "got $expect_out(buffer)" verbose -log "got $expect_out(buffer)"
fail "$test (stopped at wrong place)" fail "$test (stopped at wrong place)"
return -1 return -1