From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 26074 invoked by alias); 16 May 2011 11:24:32 -0000 Received: (qmail 26063 invoked by uid 22791); 16 May 2011 11:24:29 -0000 X-SWARE-Spam-Status: No, hits=-1.8 required=5.0 tests=AWL,BAYES_00,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,RCVD_IN_DNSWL_LOW,RFC_ABUSE_POST,TW_GJ X-Spam-Check-By: sourceware.org Received: from mail-vx0-f169.google.com (HELO mail-vx0-f169.google.com) (209.85.220.169) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Mon, 16 May 2011 11:24:12 +0000 Received: by vxk20 with SMTP id 20so4020265vxk.0 for ; Mon, 16 May 2011 04:24:11 -0700 (PDT) Received: by 10.220.201.8 with SMTP id ey8mr364619vcb.103.1305545051553; Mon, 16 May 2011 04:24:11 -0700 (PDT) MIME-Version: 1.0 Received: by 10.220.188.139 with HTTP; Mon, 16 May 2011 04:23:51 -0700 (PDT) In-Reply-To: References: From: Kevin Pouget Date: Mon, 16 May 2011 11:24:00 -0000 Message-ID: Subject: Re: [RFC] Python Finish Breakpoints To: gdb@sourceware.org, pmuldoon@redhat.com, Doug Evans Content-Type: text/plain; charset=ISO-8859-1 X-IsSubscribed: yes Mailing-List: contact gdb-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-owner@sourceware.org X-SW-Source: 2011-05/txt/msg00074.txt.bz2 Hello, I prepared some tests which show the behavior of this new Breakpoint type in various environment: - normal termination, forced termination (gdb return) - longjmp - c++ exception - breakpoint condition there is two bits which differs from what I expected, in the breakpoint conditions * setting a BP in a DUMMY_FRAME: the `stop' function will be triggerd, but it's return value (stop or continue/booleans) won't be taken into account. I'm not sure whether the kind of FinishBreakpoint should be forbidden or not, because I may want to track ALL the calls to that function, including GDB inferior calls * the "normal_stop" notification if not triggered during condition evaluation, so the `out_of_scope_notif' flag is not turn off (more generally, the notification should be disabled when the breakpoint is hit, and not when GDB stops) otherwise, for c++ exception, the return PC of a frame surrounded by a try/catch is at the end of the catch, so with try { fct_1() } catch { ... }, a FinishBreakpoint in fct_1 will be correcty catch, but it won't work for function nested within fct_1. Does this testsuite match your expectations, or would you like it to be more advanced? (I'll need more hints in this case) cordially, Kevin diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.cc b/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.cc new file mode 100644 index 0000000..a0eea06 --- /dev/null +++ b/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.cc @@ -0,0 +1,59 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2011 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 . +*/ + + +#include + +void +throw_exception_1 (int e) +{ + throw new int (e); +} + +void +throw_exception (int e) +{ + throw_exception_1 (e); +} + +int +main (void) +{ + int i; + try + { + throw_exception_1 (10); + } + catch (const int *e) + { + std::cerr << "Exception #" << *e << std::endl; + } + i += 1; /* Break after exception 1. */ + + try + { + throw_exception (10); + } + catch (const int *e) + { + std::cerr << "Exception #" << *e << std::endl; + } + i += 1; /* Break after exception 2. */ + + return i; +} diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.exp b/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.exp new file mode 100644 index 0000000..e74023d --- /dev/null +++ b/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.exp @@ -0,0 +1,59 @@ +# Copyright (C) 2011 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 tests the mechanism +# exposing values to Python. + +if $tracelevel then { + strace $tracelevel +} + +load_lib gdb-python.exp + +set testfile "py-finish-breakpoint-cc" +set srcfile ${testfile}.cc +set binfile ${objdir}/${subdir}/${testfile} +set pyfile ${srcdir}/${subdir}/${testfile}.py + +# Start with a fresh gdb. +gdb_exit +gdb_start +gdb_reinitialize_dir $srcdir/$subdir +gdb_load ${binfile} + +if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug c++}] != "" } { + untested "Couldn't compile ${srcfile}" + return -1 +} + +if ![runto_main] then { + fail "Cannot run to main." + return 0 +} + +gdb_test "source $pyfile" ".*Python script imported.*" \ + "import python scripts" + +gdb_test "break [gdb_get_line_number "Break after exception 1."]" "Breakpoint.* at .*" \ + "set watchdog after the exception 1" +gdb_test "break [gdb_get_line_number "Break after exception 2."]" "Breakpoint.* at .*" \ + "set watchdog after the exception 2" + +gdb_test "python ExceptionBreakpoint()" "ExceptionBreakpoint init" "set BP before throwing the exception" +gdb_test "python print len(gdb.breakpoints())" "4" "check number of BPs" +gdb_test "continue" ".*stopped at ExceptionFinishBreakpoint.*" "check FinishBreakpoint in catch()" +gdb_test "python print len(gdb.breakpoints())" "4" "check finish BP removal" + +gdb_test "continue" ".*exception did not finish.*" "FinishBreakpoint with exception thrown not caught" diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.py b/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.py new file mode 100644 index 0000000..d0dfe2f --- /dev/null +++ b/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.py @@ -0,0 +1,43 @@ +# Copyright (C) 2011 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 tests python Finish +# Breakpoints. + +class ExceptionBreakpoint(gdb.Breakpoint): + def __init__(self): + gdb.Breakpoint.__init__(self, spec="throw_exception_1", internal=1) + self.silent = True + print "ExceptionBreakpoint init" + + def stop(self): + ExceptionFinishBreakpoint(gdb.newest_frame()) + return False + +class ExceptionFinishBreakpoint(gdb.FinishBreakpoint): + def __init__(self, frame): + gdb.FinishBreakpoint.__init__(self, frame, internal=1) + self.silent = True; + + def stop(self): + print "stopped at ExceptionFinishBreakpoint" + gdb.post_event(self.delete) + return True + + def out_of_scope(self): + print "exception did not finish ..." + + +print "Python script imported" diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint.c b/gdb/testsuite/gdb.python/py-finish-breakpoint.c new file mode 100644 index 0000000..32b8b38 --- /dev/null +++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.c @@ -0,0 +1,82 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2011 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 . +*/ +#include + +int increase_1(int *a) +{ + *a += 1; + return -5; +} + +void increase(int *a) +{ + increase_1(a); +} + +int +test_1(int i, int j) +{ + return i == j; +} + +int +test(int i, int j) +{ + return test_1(i, j); +} + +int +call_longjmp_1 (jmp_buf *buf) +{ + longjmp (*buf, 1); +} + +int +call_longjmp (jmp_buf *buf) +{ + call_longjmp_1 (buf); +} + + +int main (int argc, char *argv[]) +{ + jmp_buf env; + int foo = 5; + int bar = 42; + int i, j; + + i = 0 ; + /* Break at increase. */ + increase (&i) ; + increase (&i) ; + increase (&i) ; + + for (i = 0; i < 10; i++) + { + j += 1; /* Condition Break. */ + } + + if (setjmp (env) == 0) /* longjmp caught */ + { + call_longjmp (&env); + } + else + j += 1; /* after longjmp. */ + + return j; /* Break at end. */ +} diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint.exp b/gdb/testsuite/gdb.python/py-finish-breakpoint.exp new file mode 100644 index 0000000..65eebc9 --- /dev/null +++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.exp @@ -0,0 +1,183 @@ +# Copyright (C) 2011 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 tests the mechanism +# exposing values to Python. + +if $tracelevel then { + strace $tracelevel +} + +load_lib gdb-python.exp + +set testfile "py-finish-breakpoint" +set srcfile ${testfile}.c + +if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile}] } { + return -1 +} + +set remote_python_file [remote_download host ${srcdir}/${subdir}/${testfile}.py] + + +# Skip all tests if Python scripting is not enabled. +if { [skip_python_tests] } { continue } + +# +# Test FinishBreakpoint in function returned by longjmp +# + +clean_restart ${testfile} + +if ![runto call_longjmp_1] then { + perror "couldn't run to breakpoint call_longjmp" + continue +} + +gdb_test "source $remote_python_file" ".*Python script imported.*" \ + "import python scripts" + +gdb_test "python ljmpBP = LongjmpFinishBreakpoint(gdb.newest_frame())" \ + "LongjmpFinishBreakpoint init" \ + "set finish breakpoint" +gdb_test "break [gdb_get_line_number "after longjmp."]" "Breakpoint.* at .*" \ + "set BP after the jump" +gdb_test "continue" ".*Longjmp didn't finish.*" "check FinishBP out of scope notification" + +# +# Test FinishBreakpoint in BP condition evaluation +# (finish in dummy frame) +# + +clean_restart ${testfile} + +set cond_line [gdb_get_line_number "Condition Break."] +if ![runto_main] then { + fail "Cannot run to main." + return 0 +} + +gdb_test "source $remote_python_file" ".*Python script imported.*" \ + "import python scripts" + +gdb_test "break ${cond_line} if test_1(i,8)" ".*Breakpoint .* at .*" "set conditional BP" +gdb_test "python TestBreakpoint()" "TestBreakpoint init" "set BP in condition" + + +set msg "check FinishBreakpoint don't stop in GDB Dummy Frame" +gdb_test_multiple "continue" $msg { + -re ".*test don't stop 2.*test stop.*test don't stop 4.*" { + pass $msg + } + -re ".*test don't stop 2.*test stop.*$gdb_prompt" { + fail $msg + } +} + +gdb_test "print i" "8" "check stopped location" + +# +# Test FinishBreakpoint in BP condition evaluation +# (finish in normal frame) +# + +clean_restart ${testfile} + +gdb_test "source $remote_python_file" ".*Python script imported.*" \ + "import python scripts" + +if ![runto_main] then { + fail "Cannot run to main." + return 0 +} + +gdb_test "break ${cond_line} if test(i,8)" ".*Breakpoint .* at .*" "set conditional BP" +gdb_test "python TestBreakpoint()" "TestBreakpoint init" "set BP in condition" + +gdb_test "continue" ".*test don't stop 1.*test don't stop 2.*test stop.*Error in testing breakpoint condition.*The program being debugged stopped while in a function called from GDB.*" \ + "stop in condition function" + +setup_kfail "normal_stop_notification not triggered during condition evaluation" *-*-* +gdb_test "python print gdb.breakpoints()\[2\].out_of_scope_notif" ".*False.*" "check out_of_scope notification disabled" +gdb_test_no_output "python gdb.breakpoints()\[2\].out_of_scope_notif = False" "reestablish correct value" + +gdb_test "continue" "Continuing.*" "finish condition evaluation" +gdb_test "continue" "Breakpoint.*" "stop at conditional breakpoint" +gdb_test "print i" "8" "check stopped location" + +# +# Test FinishBreakpoint in normal conditions +# + +clean_restart ${testfile} + +if ![runto_main] then { + fail "Cannot run to main." + return 0 +} +gdb_test_no_output "set confirm off" "disable confirmation" +gdb_test "source $remote_python_file" ".*Python script imported.*" \ + "import python scripts" +gdb_test "python MyBreakpoint(\"increase_1\")" ".*Breakpoint 2.*" \ + "create Python function breakpoint" +gdb_test "continue" ".*Arrived at MyBreakpoint with 0.*" "check MyBreakpoint hit" + +# set FinishBreakpoint + +gdb_test "python finishbp = MyFinishBreakpoint (gdb.parse_and_eval (\"a\"), gdb.selected_frame ())" \ + ".*Breakpoint 3.*" "set FinishBreakpoint" +gdb_test "python print finishbp.out_of_scope_notif" ".*True.*" \ + "check out_of_scope_notif at init" +gdb_test "python print finishbp.return_value" ".*None.*" \ + "check return_value at init" + +# check normal bp hit + +gdb_test "continue" ".*MyFinishBreakpoint stop with.*#0.*increase.*" \ + "check MyFinishBreakpoint hit" +gdb_test "python print finishbp.return_value" ".*-5.*" "check return_value" +gdb_test "python print finishbp.out_of_scope_notif" ".*False.*" \ + "check out_of_scope_notif disabled after hit" +gdb_test "finish" ".*main.*" "return to main()" +gdb_test "python print finishbp.return_value" ".*None.*" "check return_value" + +# check forced return / check out of scpop +gdb_test_no_output "python finishbp.out_of_scope_notif = True" \ + "re-enable out_of_scope_notif" + +gdb_test "continue" ".*Arrived at MyBreakpoint with.*" "check MyBreakpoint second hit" +gdb_test "up" ".*increase_1.*" "go one frame up" +gdb_test_no_output "return" "return from the frame" +gdb_test "python print finishbp.check_scope()" ".*MyFinishBreakpoint out of scope.*True.*" \ + "go one frame up" + +# check forced return / automatic notification + +gdb_test_no_output "python finishbp.out_of_scope_notif = True" \ + "re-enable out_of_scope_notif" + +gdb_test "continue" ".*Arrived at MyBreakpoint with.*" "check MyBreakpoint third hit" +gdb_test "up" ".*increase_1.*" "go one frame up" +gdb_test_no_output "return" "return from the frame" +gdb_test "next" ".*MyFinishBreakpoint out of scope.*" "check Finish breakpoint discard" +gdb_test "python print finishbp.out_of_scope_notif" ".*False.*" "check out_of_scope_notif" + +# check FinishBreakpoint in main + +gdb_test "python MyFinishBreakpoint (None, gdb.selected_frame ())" \ + ".*ValueError: \"FinishBreakpoint\" not meaningful in the outermost frame..*" \ + "check FinishBP not allowed in main" + + diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint.py b/gdb/testsuite/gdb.python/py-finish-breakpoint.py new file mode 100644 index 0000000..f014cc6 --- /dev/null +++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.py @@ -0,0 +1,85 @@ +# Copyright (C) 2011 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 tests python Finish +# Breakpoints. + +class MyBreakpoint(gdb.Breakpoint): + def stop(self): + val = gdb.parse_and_eval ("a") + print "Arrived at MyBreakpoint with %d" % int(val.dereference()) + return True + +class MyFinishBreakpoint(gdb.FinishBreakpoint): + def __init__(self, val, frame): + super (MyFinishBreakpoint, self).__init__ (frame) + print "MyFinishBreakpoint init" + self.val = val + + def stop(self): + print "MyFinishBreakpoint stop with %d" % int(self.val.dereference()) + gdb.execute("where 1") + return True + + def out_of_scope(self): + print "MyFinishBreakpoint out of scope..." + +test_finish_bp = None +class TestBreakpoint(gdb.Breakpoint): + def __init__(self): + gdb.Breakpoint.__init__(self, spec="test_1", internal=1) + self.silent = True + self.finish = None + print "TestBreakpoint init" + + def stop(self): + global test_finish_bp + if (self.finish == None): + self.finish = TestFinishBreakpoint(gdb.newest_frame()) + test_finish_bp = self.finish + return False + + +class TestFinishBreakpoint(gdb.FinishBreakpoint): + def __init__(self, frame): + gdb.FinishBreakpoint.__init__(self, frame, internal=1) + self.count = 0 + + def stop(self): + self.count += 1 + if (self.count == 3): + print "test stop ..." + return True + else: + print "test don't stop %d" % self.count + return False + + + def out_of_scope(self): + print "test didn't finish ..." + +class LongjmpFinishBreakpoint(gdb.FinishBreakpoint): + def __init__(self, frame): + gdb.FinishBreakpoint.__init__(self, frame, internal=1) + print "LongjmpFinishBreakpoint init" + + def stop(self): + print "Stopped at LongjmpFinishBreakpoint" + + + def out_of_scope(self): + print "Longjmp didn't finish ..." + +print "Python script imported" -- 1.7.4.4