diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index f400a7b4e32..6fa9ad56ad2 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,9 @@ +2016-08-24 Kevin Buettner + + * gdb.python/py-recurse-unwind.c: New file. + * gdb.python/py-recurse-unwind.py: New file. + * gdb.python/py-recurse-unwind.exp: New file. + 2016-08-24 Simon Marchi * gdb.base/set-inferior-tty.exp: New file. diff --git a/gdb/testsuite/gdb.python/py-recurse-unwind.c b/gdb/testsuite/gdb.python/py-recurse-unwind.c new file mode 100644 index 00000000000..02a835a833c --- /dev/null +++ b/gdb/testsuite/gdb.python/py-recurse-unwind.c @@ -0,0 +1,42 @@ +/* This test program is part of GDB, the GNU debugger. + + Copyright 2016 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 . */ + +/* This is the test program loaded into GDB by the py-recurse-unwind test. */ + +void +ccc (int arg) +{ +} + +void +bbb (int arg) +{ + ccc (789); +} + +void +aaa (int arg) +{ + bbb (456); +} + +int +main () +{ + aaa (123); + return 0; +} diff --git a/gdb/testsuite/gdb.python/py-recurse-unwind.exp b/gdb/testsuite/gdb.python/py-recurse-unwind.exp new file mode 100644 index 00000000000..fe17d3fe647 --- /dev/null +++ b/gdb/testsuite/gdb.python/py-recurse-unwind.exp @@ -0,0 +1,75 @@ +# Copyright (C) 2016 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 . + +# This file is part of the GDB testsuite. It is used to verify that +# GDB does not recurse infinitely when calling gdb.parse_and_eval() in +# the course of sniffing a frame in a Python unwinder. + +# The unwinder has been constructed so that, should recursion occur, +# it will be detected in the unwinder so that we won't need to wait +# for a timeout. + + +load_lib gdb-python.exp + +standard_testfile + +if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile}] } { + return -1 +} + +# Skip all tests if Python scripting is not enabled. +if { [skip_python_tests] } { continue } + +set pyfile [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py] + +gdb_test "source ${pyfile}" "Python script imported" \ + "import python scripts" + +# The following tests require execution. + +if ![runto_main] then { + fail "Can't run to main" + return 0 +} + +gdb_breakpoint "ccc" + +gdb_continue_to_breakpoint "ccc" + +# If the unwinder is active, the usage count will increment while +# running to the breakpoint. Reset it prior to doing the backtrace. +gdb_test_no_output "python TestUnwinder.reset_count()" + +# The python based unwinder should be called a number of times while +# generating the backtrace, but its sniffer always returns None. So +# it doesn't really contribute to generating any of the frames below. +# +# But that's okay. Our goal here is to make sure that GDB doesn't +# get hung up in potentially infinite recursion when invoking the +# Python-based unwinder. + +setup_kfail "gdb/19927" "*-*-*" +gdb_test_sequence "bt" "backtrace" { + "\\r\\n#0 .* ccc \\(arg=789\\) at " + "\\r\\n#1 .* bbb \\(arg=456\\) at " + "\\r\\n#2 .* aaa \\(arg=123\\) at " + "\\r\\n#3 .* main \\(.*\\) at" +} + +# Test that the python-based unwinder / sniffer was actually called +# during generation of the backtrace. +setup_kfail "gdb/19927" "*-*-*" +gdb_test "python print(TestUnwinder.count > 0)" "True" diff --git a/gdb/testsuite/gdb.python/py-recurse-unwind.py b/gdb/testsuite/gdb.python/py-recurse-unwind.py new file mode 100644 index 00000000000..1da7aca9339 --- /dev/null +++ b/gdb/testsuite/gdb.python/py-recurse-unwind.py @@ -0,0 +1,68 @@ +# Copyright (C) 2016 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 . + +# This unwinder never does any unwinding. It'll (pretend to) "sniff" +# the frame and ultimately return None, indicating that actual unwinding +# should be performed by some other unwinder. +# +# But, prior to returning None, it will attempt to obtain the value +# associated with a symbol via a call to gdb.parse_and_eval(). In +# the course of doing this evaluation, GDB will potentially access +# some frames, leading to the possibility of a recursive invocation of +# this unwinder. If that should happen, code contained herein detects +# that and prints a message which will cause some of the associated +# tests to FAIL. + +import gdb +from gdb.unwinder import Unwinder + +class TestUnwinder(Unwinder): + + count = 0 + + @classmethod + def reset_count (cls): + cls.count = 0 + + @classmethod + def inc_count (cls): + cls.count += 1 + + def __init__(self): + Unwinder.__init__(self, "test unwinder") + self.recurse_level = 0 + + def __call__(self, pending_frame): + + + if self.recurse_level > 0: + gdb.write("TestUnwinder: Recursion detected - returning early.\n") + return None + + self.recurse_level += 1 + TestUnwinder.inc_count() + + try: + val = gdb.parse_and_eval("undefined_symbol") + + except Exception as arg: + pass + + self.recurse_level -= 1 + + return None + +gdb.unwinder.register_unwinder(None, TestUnwinder(), True) +gdb.write("Python script imported\n")