This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [PATCH] improve python finish breakpoint for exceptions/longjmp case.


On 30/10/2012 5:40 PM, Jan Kratochvil wrote:
> On Thu, 25 Oct 2012 21:23:19 +0200, Andrew Burgess wrote:
> 
> Anyway we agree it is not transparent to "finish" anyway but this is more
> a problem there are no observer-like breakpoints:
> 
> ==> finish.c <==
> void g (void) {}
> void f (void) {
>   g ();
> }
> int main (void) {
>   f ();
>   return 0;
> }
> 
> ==> finish.cmd <==
> file ./finish
> start
> step
> break g
> commands
>  echo hook-g\n
>  continue
> end
> finish
> 
> -------------------
> 
> hook-g
> Actual:
> [Inferior 1 (process 13204) exited normally]
> (gdb) _
> Expected:
> main () at finish.c:7
> 7	  return 0;
> (gdb) _
> 

So currently the finish command stops whenever gdb stops, even though in this case once we stop (at g) we then automatically continue again.

In the example is it that you'd like to consider the "g" breakpoint observer-like (just because it contains a continue), or are you suggesting that the "finish" command should not auto-terminate once gdb stops?

I can't really see how the above example relates to the issue with my patch, however, I have a new patch in which I believe the level of risk of code corruption should be less.

I now create the longjmp breakpoints using the actual frame we want to stop in, not the null_frame_id.  This means that when we hit the longjmp we'll immediately stop triggering the out-of-scope callback and deleting the finish breakpoint.  This seems like a better solution to me, it's more "finish" like in behaviour.  The documentation for "FinishBreakpoint" makes no claim one way or another on if we'll stop for the out-of-scope call; it would be nice if we could control the stop behaviour from that callback, but I'd rather leave that change for another day.

As far as I can see the only remaining issue is that using other commands that set/clear the longjmp breakpoints once a FinishBreakpoint is active (finish/step/etc) will remove the longjmp breakpoint, we'll then fallback to only calling the out-of-scope callback at the next stop.  To solve this I'd like to change the longjmp breakpoint mechanism to support having multiple longjmp breakpoint sets active at once; but I'd rather do this in a follow-up patch, and as the new behaviour is no worse than the original behaviour this feels like a reasonable compromise to me (but you might disagree).

I just remembered you asked me to come up with a better name for the new test than py-finish-breakpoint3, I forgot to do that in the patch version below, but I'll change it to something like py-finish-breakpoint-threads.

Let me know what you think,

Thanks,
Andrew
 

gdb/ChangeLog

2012-11-02  Andrew Burgess  <aburgess@broadcom.com>

	* python/py-finishbreakpoint.c (struct finish_breakpoint_object)
	<initiating_frame>: New field.
	(bpfinishpy_post_stop_hook): Disable the longjmp breakpoints when
	we stop at a finish breakpoint.  Have the finish breakpoint
	deleted at the next stop, wherever that might be.
	(bpfinishpy_init): Set longjmp breakpoints.  Remember frame we're
	in when creating the finish breakpoint.
	(struct bpfinishpy_out_of_scope_data): New structure for passing
	parameters to out of scope callback.
	(bpfinishpy_detect_out_scope_cb): Look for frame we are hoping to
	finish when deciding if we're out of scope, not frame of parent.
	Check we're stopped in correct thread, or that the breakpoint
	thread has exited before we declare a breakpoint out of scope.
	(bpfinishpy_handle_stop): Pass parameter struct to callback.
	(bpfinishpy_handle_exit): Pass parameter struct to callback.

diff --git a/gdb/python/py-finishbreakpoint.c b/gdb/python/py-finishbreakpoint.c
index 56ab775..4906fac 100644
--- a/gdb/python/py-finishbreakpoint.c
+++ b/gdb/python/py-finishbreakpoint.c
@@ -53,6 +53,9 @@ struct finish_breakpoint_object
      the function; Py_None if the value is not computable; NULL if GDB is
      not stopped at a FinishBreakpoint.  */
   PyObject *return_value;
+  /* The initiating frame for this operation, used to decide when we have
+     left this frame.  */
+  struct frame_id initiating_frame;
 };
 
 /* Python function to get the 'return_value' attribute of
@@ -141,6 +144,10 @@ bpfinishpy_post_stop_hook (struct breakpoint_object *bp_obj)
       /* Can't delete it here, but it will be removed at the next stop.  */
       disable_breakpoint (bp_obj->bp);
       gdb_assert (bp_obj->bp->disposition == disp_del);
+      bp_obj->bp->disposition = disp_del_at_next_stop;
+
+      /* Disable all the longjmp breakpoints too.  */
+      delete_longjmp_breakpoint_at_next_stop (bp_obj->bp->thread);
     }
   if (except.reason < 0)
     {
@@ -161,7 +168,7 @@ bpfinishpy_init (PyObject *self, PyObject *args, PyObject *kwargs)
   PyObject *frame_obj = NULL;
   int thread;
   struct frame_info *frame, *prev_frame = NULL;
-  struct frame_id frame_id;
+  struct frame_id prev_frame_id, init_frame_id;
   PyObject *internal = NULL;
   int internal_bp = 0;
   CORE_ADDR finish_pc, pc;
@@ -184,6 +191,8 @@ bpfinishpy_init (PyObject *self, PyObject *args, PyObject *kwargs)
 
   if (frame == NULL)
     goto invalid_frame;
+
+  init_frame_id = get_frame_id (frame);
   
   TRY_CATCH (except, RETURN_MASK_ALL)
     {
@@ -201,8 +210,8 @@ bpfinishpy_init (PyObject *self, PyObject *args, PyObject *kwargs)
         }
       else
         {
-          frame_id = get_frame_id (prev_frame);
-          if (frame_id_eq (frame_id, null_frame_id))
+          prev_frame_id = get_frame_id (prev_frame);
+          if (frame_id_eq (prev_frame_id, null_frame_id))
             PyErr_SetString (PyExc_ValueError,
                              _("Invalid ID for the `frame' object."));
         }
@@ -295,11 +304,18 @@ bpfinishpy_init (PyObject *self, PyObject *args, PyObject *kwargs)
                          AUTO_BOOLEAN_TRUE,
                          &bkpt_breakpoint_ops,
                          0, 1, internal_bp, 0);
+      set_longjmp_breakpoint (inferior_thread (), init_frame_id);
+
+      /* Set frame to NULL for sanity, creating the breakpoint could cause
+	 us to switch threads, thus blowing away the frame cache, rendering
+	 the frame pointer invalid.  */
+      frame = NULL;
     }
   GDB_PY_SET_HANDLE_EXCEPTION (except);
   
-  self_bpfinish->py_bp.bp->frame_id = frame_id;
+  self_bpfinish->py_bp.bp->frame_id = prev_frame_id;
   self_bpfinish->py_bp.is_finish_bp = 1;
+  self_bpfinish->initiating_frame = init_frame_id;
   
   /* Bind the breakpoint with the current program space.  */
   self_bpfinish->py_bp.bp->pspace = current_program_space;
@@ -329,9 +345,18 @@ bpfinishpy_out_of_scope (struct finish_breakpoint_object *bpfinish_obj)
           gdbpy_print_stack ();
     }
 
+  delete_longjmp_breakpoint_at_next_stop (bp_obj->bp->thread);
   delete_breakpoint (bpfinish_obj->py_bp.bp);
 }
 
+/* Structure used to pass parameter to the out of scope check.  */
+
+struct bpfinishpy_out_of_scope_data
+{
+  struct breakpoint *bp;
+  int thread;
+};
+
 /* Callback for `bpfinishpy_detect_out_scope'.  Triggers Python's
    `B->out_of_scope' function if B is a FinishBreakpoint out of its scope.  */
 
@@ -339,25 +364,39 @@ static int
 bpfinishpy_detect_out_scope_cb (struct breakpoint *b, void *args)
 {
   volatile struct gdb_exception except;
-  struct breakpoint *bp_stopped = (struct breakpoint *) args;
+  struct bpfinishpy_out_of_scope_data *data =
+    (struct bpfinishpy_out_of_scope_data *) args;
+  struct breakpoint *bp_stopped = data->bp;
+  int thread_num = data->thread;
   PyObject *py_bp = (PyObject *) b->py_bp_object;
   struct gdbarch *garch = b->gdbarch ? b->gdbarch : get_current_arch ();
-  
+
   /* Trigger out_of_scope if this is a FinishBreakpoint and its frame is
-     not anymore in the current callstack.  */
-  if (py_bp != NULL && b->py_bp_object->is_finish_bp)
+     no longer in the current callstack, or the thread for the
+     FinishBreakpoint has gone away.  */
+  if (py_bp != NULL
+      && b->py_bp_object->is_finish_bp
+      && (b->thread == -1
+	  || b->thread == thread_num
+	  || find_thread_id (b->thread) == NULL
+	  || is_exited (thread_id_to_pid (b->thread))))
     {
       struct finish_breakpoint_object *finish_bp =
           (struct finish_breakpoint_object *) py_bp;
 
+      /* All finish breakpoints are created for a specific thread.  */
+      gdb_assert (b->thread != -1);
+
       /* Check scope if not currently stopped at the FinishBreakpoint.  */
       if (b != bp_stopped)
         {
           TRY_CATCH (except, RETURN_MASK_ALL)
             {
+	      struct frame_id fid = finish_bp->initiating_frame;
+
               if (b->pspace == current_inferior ()->pspace
                   && (!target_has_registers
-                      || frame_find_by_id (b->frame_id) == NULL))
+                      || frame_find_by_id (fid) == NULL))
                 bpfinishpy_out_of_scope (finish_bp);
             }
           if (except.reason < 0)
@@ -377,11 +416,18 @@ bpfinishpy_detect_out_scope_cb (struct breakpoint *b, void *args)
 static void
 bpfinishpy_handle_stop (struct bpstats *bs, int print_frame)
 {
-  struct cleanup *cleanup = ensure_python_env (get_current_arch (),
-                                               current_language);
+  struct bpfinishpy_out_of_scope_data data;
+  struct cleanup *cleanup;
+
+  if (!find_thread_ptid (inferior_ptid))
+      return;
 
-  iterate_over_breakpoints (bpfinishpy_detect_out_scope_cb,
-                            bs == NULL ? NULL : bs->breakpoint_at);
+  cleanup = ensure_python_env (get_current_arch (), current_language);
+
+  data.bp = (bs == NULL ? NULL : bs->breakpoint_at);
+  data.thread = inferior_thread ()->num;
+
+  iterate_over_breakpoints (bpfinishpy_detect_out_scope_cb, &data);
 
   do_cleanups (cleanup);
 }
@@ -392,10 +438,15 @@ bpfinishpy_handle_stop (struct bpstats *bs, int print_frame)
 static void
 bpfinishpy_handle_exit (struct inferior *inf)
 {
-  struct cleanup *cleanup = ensure_python_env (target_gdbarch,
-                                               current_language);
+  struct bpfinishpy_out_of_scope_data data;
+  struct cleanup *cleanup;
+
+  cleanup = ensure_python_env (target_gdbarch, current_language);
+
+  data.bp = NULL;
+  data.thread = -1;
 
-  iterate_over_breakpoints (bpfinishpy_detect_out_scope_cb, NULL);
+  iterate_over_breakpoints (bpfinishpy_detect_out_scope_cb, &data);
 
   do_cleanups (cleanup);
 }


gdb/testsuite/ChangeLog

2012-11-02  Andrew Burgess  <aburgess@broadcom.com>

	Additional testing for FinishBreakpoint.
	* gdb.python/py-finish-breakpoint2.cc: Add extra levels of nesting
	to allow more testing opportunities.
	* gdb.python/py-finish-breakpoint2.exp: Additional test cases.
	* gdb.python/py-finish-breakpoint3.c: New file.
	* gdb.python/py-finish-breakpoint3.exp: New file.
	* gdb.python/py-finish-breakpoint3.py: New file.

diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint2.cc b/gdb/testsuite/gdb.python/py-finish-breakpoint2.cc
index 8cc756f..930e6bc 100644
--- a/gdb/testsuite/gdb.python/py-finish-breakpoint2.cc
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint2.cc
@@ -22,7 +22,8 @@
 void
 throw_exception_1 (int e)
 {
-  throw new int (e);
+  if (e > 5)
+    throw new int (e);
 }
 
 void
@@ -32,28 +33,79 @@ throw_exception (int e)
 }
 
 int
-main (void)
+do_test (int e)
 {
-  int i;
+  int i = 0;
   try
     {
-      throw_exception_1 (10);
+      throw_exception_1 (e);
+
+      i += 1;
     }
   catch (const int *e)
     {
         std::cerr << "Exception #" << *e << std::endl;
     }
-  i += 1; /* Break after exception 1.  */
+  i += 1;
 
   try
     {
-      throw_exception (10);
+      throw_exception (e);
+
+      i += 1;
     }
   catch (const int *e)
     {
         std::cerr << "Exception #" << *e << std::endl;
     }
-  i += 1; /* Break after exception 2.  */
+  i += 1;
+
+  return i;
+}
+
+int
+call_do_test (int e)
+{
+  int i;
+
+  i = do_test (e);
+
+  throw_exception_1 (e);
+
+  return i;
+}
+
+int
+do_nested_test (int e)
+{
+  int i = 0;
+
+  try
+    {
+      call_do_test (e);
+
+      i += 1;
+    }
+  catch (const int *e)
+    {
+      std::cerr << "Exception #" << *e << std::endl;
+    }
+  i += 1;
 
   return i;
 }
+
+
+int
+main (void)
+{
+  int i = 0;
+
+  i += do_test (10);
+
+  i += do_test (4);
+
+  i += do_nested_test (10);
+
+  return i; /* Additional breakpoint */
+}
diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint2.exp b/gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
index 3b08ef8..a3a12fd 100644
--- a/gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
@@ -37,7 +37,7 @@ if ![runto_main] then {
 # Check FinishBreakpoints against C++ exceptions
 #
 
-gdb_breakpoint [gdb_get_line_number "Break after exception 2"]
+gdb_breakpoint [gdb_get_line_number "Additional breakpoint"]
 
 gdb_test "source $pyfile" ".*Python script imported.*" \
          "import python scripts"
@@ -47,9 +47,43 @@ gdb_test "continue" "Breakpoint .*throw_exception_1.*" "run to exception 1"
 
 gdb_test "python print len(gdb.breakpoints())" "3" "check BP count"
 gdb_test "python ExceptionFinishBreakpoint(gdb.newest_frame())" "init ExceptionFinishBreakpoint" "set FinishBP after the exception"
-gdb_test "continue" ".*stopped at ExceptionFinishBreakpoint.*" "check FinishBreakpoint in catch()"
+gdb_test "continue" ".*do_test \\(e=10\\).*catch \\(const int \\*e\\).*exception did not finish.*" "FinishBreakpoint with exception thrown caught in parent"
 gdb_test "python print len(gdb.breakpoints())" "3" "check finish BP removal"
 
 gdb_test "continue" ".*Breakpoint.* throw_exception_1.*" "continue to second exception"
 gdb_test "python ExceptionFinishBreakpoint(gdb.newest_frame())" "init ExceptionFinishBreakpoint" "set FinishBP after the exception"
-gdb_test "continue" ".*exception did not finish.*" "FinishBreakpoint with exception thrown not caught"
+gdb_test "continue" ".*do_test \\(e=10\\).*catch \\(const int \\*e\\).*exception did not finish.*" "FinishBreakpoint with exception thrown caught in grandparent"
+gdb_test "python print len(gdb.breakpoints())" "3" "check finish BP removal"
+
+gdb_test "continue" ".*Breakpoint.* throw_exception_1.*" "continue to first no throw test"
+gdb_test "python ExceptionFinishBreakpoint(gdb.newest_frame())" "init ExceptionFinishBreakpoint" "set FinishBP after the exception"
+gdb_test "continue" ".*stopped at ExceptionFinishBreakpoint.*" "FinishBreakpoint with exception not thrown"
+gdb_test "python print len(gdb.breakpoints())" "3" "check finish BP removal"
+
+gdb_test "continue" ".*Breakpoint.* throw_exception_1.*" "continue to second no throw test"
+gdb_test "python ExceptionFinishBreakpoint(gdb.newest_frame())" "init ExceptionFinishBreakpoint" "set FinishBP after the exception"
+gdb_test "continue" ".*stopped at ExceptionFinishBreakpoint.*" "FinishBreakpoint with exception not thrown"
+gdb_test "python print len(gdb.breakpoints())" "3" "check finish BP removal"
+
+# Now exercies the nested test example, we're creating an
+# ExceptionFinishBreakpoint inside a frame, then going to continue into
+# further child frames before using the "finish" command, finally, we'll
+# continue, and look for the original ExceptionFinishBreakpoint frame to
+# finish.
+
+gdb_breakpoint "call_do_test"
+gdb_test "continue" ".*Breakpoint.* call_do_test.*" "continue to nested test."
+gdb_test "python print len(gdb.breakpoints())" "4" "check BP count before nested test."
+gdb_test "python ExceptionFinishBreakpoint(gdb.newest_frame())" "init ExceptionFinishBreakpoint" "set FinishBP after the exception"
+gdb_test "continue" ".*Breakpoint.* throw_exception_1.*" "continue to second no throw test"
+gdb_test "finish" ".*do_test \\(e=10\\).*catch \\(const int \\*e\\).*" "finish with exception being thrown, caught in parent"
+
+gdb_test "continue" ".*Breakpoint.* throw_exception_1.*" "continue to second no throw test"
+gdb_test "finish" ".*do_test \\(e=10\\).*catch \\(const int \\*e\\).*" "finish with exception being thrown, caught in grand-parent"
+
+gdb_test "continue" ".*Breakpoint.* throw_exception_1.*" "continue to second no throw test"
+
+setup_kfail "BUG/1234" "*-*-*"
+gdb_test "continue" ".*catch \\(const int \\*e\\).*exception did not finish.*" "FinishBreakpoint nested with exception thrown caught in parent"
+
+gdb_test "python print len(gdb.breakpoints())" "4" "check finish BP removal"
diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint3.c b/gdb/testsuite/gdb.python/py-finish-breakpoint3.c
new file mode 100644
index 0000000..7e89024
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint3.c
@@ -0,0 +1,148 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2012 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/>.
+*/
+
+#include <pthread.h>
+#include <assert.h>
+#include <unistd.h>
+
+pthread_mutex_t lock_0 = PTHREAD_MUTEX_INITIALIZER;
+pthread_mutex_t lock_1 = PTHREAD_MUTEX_INITIALIZER;
+pthread_barrier_t barrier;
+
+void
+breakpt (int is_first_thread)
+{
+  if (is_first_thread)
+    {
+      /* Main thread.  */
+      pthread_mutex_unlock (&lock_0); /* Set thread 2 going.  */
+      pthread_mutex_lock (&lock_1); /* Wait for thread 2.  */
+    }
+  else
+    {
+      /* Other thread - Nothing.  */
+    }
+}
+
+void
+func (int is_first_thread)
+{
+  breakpt (is_first_thread);
+}
+
+static void *
+start_owner_test (void *arg)
+{
+  int i;
+
+  i = pthread_mutex_lock (&lock_1);
+  assert (i == 0);
+
+  i = pthread_barrier_wait (&barrier);
+  assert (i == 0 || i == PTHREAD_BARRIER_SERIAL_THREAD);
+
+  /* Wait for the first thread to set us going.  This lock is initially
+     held by the main thread.  */
+  i = pthread_mutex_lock (&lock_0);
+  assert (i == 0);
+
+  func (0);
+
+  /* Release the first thread to finish.  */
+  i = pthread_mutex_unlock (&lock_1);
+  assert (i == 0);
+
+  return arg;
+}
+
+void
+do_pthread_exit ()
+{
+  pthread_exit (NULL);
+}
+
+static void *
+start_exit_test (void *arg)
+{
+  do_pthread_exit ();
+}
+
+void
+final_breakpt ()
+{
+  /* Nothing.  */
+}
+
+int
+main (void)
+{
+  pthread_t thread1;
+  int i;
+
+  /* Ensure test terminates should gdb crash.  */
+  alarm (60);
+
+  /* Test 1: Create a child thread, which after some synchronisation will
+     wait on lock_0, then we call through func into the function breakpt.
+     The test has gdb stop in breakpt and create a finish breakpoint.
+     Once the main thread is in breakpt it will wait for the child thread
+     which then enters and exits the breakpt function.  Finally the
+     threads all join back in main.  We're testing the the finish
+     breakpoint does not fire for the second thread. */
+
+  i = pthread_barrier_init (&barrier, NULL, 2);
+  assert (i == 0);
+
+  i = pthread_mutex_lock (&lock_0);
+  assert (i == 0);
+
+  i = pthread_create (&thread1, NULL, start_owner_test, NULL);
+  assert (i == 0);
+
+  /* Barrier ensures that lock_0 is held by main thread, and lock_1 is held
+     by the start_owner_test thread.  */
+  i = pthread_barrier_wait (&barrier);
+  assert (i == 0 || i == PTHREAD_BARRIER_SERIAL_THREAD);
+
+  func (1);
+
+  i = pthread_join (thread1, NULL);
+  assert (i == 0);
+
+  i = pthread_barrier_destroy (&barrier);
+  assert (i == 0);
+
+  /* End of test 1.  */
+
+  /* Test 2:  Have gdb break on entry to start_exit_test function, create
+     a finish breakpoint then continue.  The child thread will then exit,
+     and so never finish.  We're checking that the out-of-scope method of
+     the finish break point does get called.  */
+
+  i = pthread_create (&thread1, NULL, start_exit_test, NULL);
+  assert (i == 0);
+
+  i = pthread_join (thread1, NULL);
+  assert (i == 0);
+
+  final_breakpt ();
+
+  /* End of test 2.  */
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint3.exp b/gdb/testsuite/gdb.python/py-finish-breakpoint3.exp
new file mode 100644
index 0000000..5590759
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint3.exp
@@ -0,0 +1,145 @@
+# Copyright (C) 2012 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/>.
+
+# This file is part of the GDB testsuite.
+
+load_lib gdb-python.exp
+
+standard_testfile
+set pyfile ${srcdir}/${subdir}/${testfile}.py
+
+if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+    return -1
+}
+
+# Check that stopping thread #2 in BREAKPT does not cause the
+# FinishBreakpoint for threda #1 to go out of scope.
+with_test_prefix "no_bp_disable" {
+    clean_restart $testfile
+
+    # Skip all tests if Python scripting is not enabled.
+    if { [skip_python_tests] } { continue }
+
+    if ![runto_main] then {
+	fail "Cannot run to main."
+	return 0
+    }
+
+    gdb_test "source $pyfile" ".*Python script imported.*" \
+	"import python scripts"
+
+    gdb_breakpoint "breakpt"
+    gdb_continue_to_breakpoint "breakpt"
+
+    gdb_test "python finishbp = MyFinishBreakpoint (gdb.newest_frame ())" \
+	"Temporary breakpoint \[0-9\]+ \[^\r\n\]+\r\nMyFinishBreakpoint init" \
+	"set FinishBreakpoint in current frame thread 1?"
+
+    gdb_test "info breakpoints" \
+	"1.*breakpoint.*keep.*y.*${hex}.*in main at.*3.*breakpoint.*del.*y.*${hex}.*in func at.*thread 1" \
+	"Check breakpoints after creating finishbp."
+
+    gdb_test "info thread" \
+	"\\* 1\[\t \]+Thread.*" \
+	"Check we're currently stopped in thread #1"
+
+    set test "continue to breakpt in thread 2"
+    gdb_test_multiple "continue" $test {
+	-re "MyFinishBreakpoint out of scope.*$gdb_prompt $" {
+	    fail $test
+	}
+	-re "Breakpoint \[0-9\]+, breakpt.*$gdb_prompt $" {
+	    pass $test
+	}
+    }
+
+    gdb_test "continue" "MyFinishBreakpoint stop.*Breakpoint \[0-9\], func.*"
+
+    gdb_test "info thread" \
+	"\\* 1\[\t \]+Thread.*" \
+	"Check we're still in thread #1 at func breakpoint"
+}
+
+# Check that thread #2 does not stop at the finish breakpoint for thread #1.
+with_test_prefix "with_bp_disable" {
+
+    # Start with a fresh gdb.
+    clean_restart ${testfile}
+
+    if ![runto_main] then {
+	fail "Cannot run to main."
+	return 0
+    }
+
+    gdb_test "source $pyfile" ".*Python script imported.*" \
+	"import python scripts"
+
+    gdb_breakpoint "breakpt"
+    gdb_test_no_output "set \$breakpt_bp_num=\$bpnum"
+
+    gdb_continue_to_breakpoint "breakpt"
+
+    gdb_test "python finishbp = MyFinishBreakpoint (gdb.newest_frame ())" \
+	"Temporary breakpoint \[0-9\]+ \[^\r\n\]+\r\nMyFinishBreakpoint init" \
+	"set FinishBreakpoint in current frame thread 1?"
+
+    gdb_test "info breakpoints" \
+	"1.*breakpoint.*keep.*y.*${hex}.*in main at.*3.*breakpoint.*del.*y.*${hex}.*in func at.*thread 1" \
+	"Check breakpoints after creating finishbp."
+
+    gdb_test "info thread" \
+	"\\* 1\[\t \]+Thread.*" \
+	"Check we're currently stopped in thread #1"
+
+    gdb_test_no_output "disable \$breakpt_bp_num"
+
+    gdb_test "continue" \
+	"MyFinishBreakpoint stop\r\n\r\nBreakpoint \[0-9\]+, func.*" \
+	"continue to func breakpoint in thread 1"
+
+    gdb_test "info thread" \
+	"\\* 1\[\t \]+Thread.*" \
+	"Check we're still in thread #1 at func breakpoint"
+}
+
+# Check that thread #2 does not stop at the finish breakpoint for thread #1.
+with_test_prefix "pthread_exit_test" {
+
+    # Start with a fresh gdb.
+    clean_restart ${testfile}
+
+    if ![runto_main] then {
+	fail "Cannot run to main."
+	return 0
+    }
+
+    gdb_test "source $pyfile" ".*Python script imported.*" \
+	"import python scripts"
+
+    gdb_breakpoint "final_breakpt"
+    gdb_breakpoint "do_pthread_exit"
+    gdb_continue_to_breakpoint "do_pthread_exit"
+
+    gdb_test "python finishbp = MyFinishBreakpoint (gdb.newest_frame ())" \
+	"Temporary breakpoint \[0-9\]+ \[^\r\n\]+\r\nMyFinishBreakpoint init" \
+	"set FinishBreakpoint in current frame thread 1?"
+
+    gdb_test "continue" \
+	"${hex} in start_thread \\(\\) from\[^\r\n\]+\r\nMyFinishBreakpoint out of scope" \
+	"detect out of scope when thread exits"
+
+}
+
+
diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint3.py b/gdb/testsuite/gdb.python/py-finish-breakpoint3.py
new file mode 100644
index 0000000..c2c72ca
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint3.py
@@ -0,0 +1,30 @@
+# Copyright (C) 2012 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/>.
+
+# This file is part of the GDB testsuite.
+
+class MyFinishBreakpoint (gdb.FinishBreakpoint):
+        def __init__(self, frame):
+                gdb.FinishBreakpoint.__init__ (self, frame)
+                print "MyFinishBreakpoint init"
+
+        def stop(self):
+                print "MyFinishBreakpoint stop"
+                return True
+
+        def out_of_scope(self):
+                print "MyFinishBreakpoint out of scope"
+
+print "Python script imported"





Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]