Throw a "quit" on a KeyboardException in py-framefilter.c

If a C-c comes while the Python code for a frame filter is running, it
will be turned into a Python KeyboardException.  It seems good for
this to be treated like a GDB quit, so this patch changes
py-framefilter.c to notice this situation and call throw_quit in this
case.

gdb/ChangeLog
2018-03-26  Tom Tromey  <tom@tromey.com>

	* python/py-framefilter.c (throw_quit_or_print_exception): New
	function.
	(gdbpy_apply_frame_filter): Use it.

gdb/testsuite/ChangeLog
2018-03-26  Tom Tromey  <tom@tromey.com>

	* gdb.python/py-framefilter.exp: Add test for KeyboardInterrupt.
	* gdb.python/py-framefilter.py (name_error): New global.
	(ErrorInName.function): Use name_error.
This commit is contained in:
Tom Tromey
2017-04-24 21:47:59 -06:00
parent 92256134f3
commit 4ca59a9f36
5 changed files with 41 additions and 4 deletions

View File

@ -1,3 +1,9 @@
2018-03-26 Tom Tromey <tom@tromey.com>
* python/py-framefilter.c (throw_quit_or_print_exception): New
function.
(gdbpy_apply_frame_filter): Use it.
2018-03-26 Tom Tromey <tom@tromey.com> 2018-03-26 Tom Tromey <tom@tromey.com>
PR cli/17716: PR cli/17716:

View File

@ -1305,6 +1305,21 @@ bootstrap_python_frame_filters (struct frame_info *frame,
return iterable.release (); return iterable.release ();
} }
/* A helper function that will either print an exception or, if it is
a KeyboardException, throw a quit. This can only be called when
the Python exception is set. */
static void
throw_quit_or_print_exception ()
{
if (PyErr_ExceptionMatches (PyExc_KeyboardInterrupt))
{
PyErr_Clear ();
throw_quit ("Quit");
}
gdbpy_print_stack ();
}
/* This is the only publicly exported function in this file. FRAME /* This is the only publicly exported function in this file. FRAME
is the source frame to start frame-filter invocation. FLAGS is an is the source frame to start frame-filter invocation. FLAGS is an
integer holding the flags for printing. The following elements of integer holding the flags for printing. The following elements of
@ -1375,7 +1390,7 @@ gdbpy_apply_frame_filter (const struct extension_language_defn *extlang,
initialization error. This return code will trigger a initialization error. This return code will trigger a
default backtrace. */ default backtrace. */
gdbpy_print_stack (); throw_quit_or_print_exception ();
return EXT_LANG_BT_NO_FILTERS; return EXT_LANG_BT_NO_FILTERS;
} }
@ -1398,7 +1413,7 @@ gdbpy_apply_frame_filter (const struct extension_language_defn *extlang,
{ {
if (PyErr_Occurred ()) if (PyErr_Occurred ())
{ {
gdbpy_print_stack (); throw_quit_or_print_exception ();
return EXT_LANG_BT_ERROR; return EXT_LANG_BT_ERROR;
} }
break; break;
@ -1423,7 +1438,7 @@ gdbpy_apply_frame_filter (const struct extension_language_defn *extlang,
/* Do not exit on error printing a single frame. Print the /* Do not exit on error printing a single frame. Print the
error and continue with other frames. */ error and continue with other frames. */
if (success == EXT_LANG_BT_ERROR) if (success == EXT_LANG_BT_ERROR)
gdbpy_print_stack (); throw_quit_or_print_exception ();
} }
return success; return success;

View File

@ -1,3 +1,9 @@
2018-03-26 Tom Tromey <tom@tromey.com>
* gdb.python/py-framefilter.exp: Add test for KeyboardInterrupt.
* gdb.python/py-framefilter.py (name_error): New global.
(ErrorInName.function): Use name_error.
2018-03-26 Tom Tromey <tom@tromey.com> 2018-03-26 Tom Tromey <tom@tromey.com>
PR backtrace/15582: PR backtrace/15582:

View File

@ -213,6 +213,12 @@ gdb_test_multiple "bt 1" $test {
} }
} }
# Now verify that we can see a quit.
gdb_test_no_output "python name_error = KeyboardInterrupt" \
"Change ErrorFilter to throw KeyboardInterrupt"
gdb_test "bt 1" "Quit" "bt 1 with KeyboardInterrupt"
# Test with no debuginfo # Test with no debuginfo
# We cannot use prepare_for_testing as we have to set the safe-patch # We cannot use prepare_for_testing as we have to set the safe-patch

View File

@ -134,13 +134,17 @@ class FrameElider ():
def filter (self, frame_iter): def filter (self, frame_iter):
return ElidingIterator (frame_iter) return ElidingIterator (frame_iter)
# This is here so the test can change the kind of error that is
# thrown.
name_error = RuntimeError
# A simple decorator that gives an error when computing the function. # A simple decorator that gives an error when computing the function.
class ErrorInName(FrameDecorator): class ErrorInName(FrameDecorator):
def __init__(self, frame): def __init__(self, frame):
FrameDecorator.__init__(self, frame) FrameDecorator.__init__(self, frame)
def function(self): def function(self):
raise RuntimeError('whoops') raise name_error('whoops')
# A filter that supplies buggy frames. Disabled by default. # A filter that supplies buggy frames. Disabled by default.
class ErrorFilter(): class ErrorFilter():