public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
From: Tom de Vries <tdevries@suse.de>
To: gdb-patches@sourceware.org
Cc: Andrew Burgess <aburgess@redhat.com>
Subject: [committed] [gdb/python] Fix gdb.python/py-finish-breakpoint2.exp for -m32
Date: Sat, 31 Dec 2022 09:10:43 +0100	[thread overview]
Message-ID: <0900fca3-4d52-f1d9-cbd6-90da923e2531@suse.de> (raw)
In-Reply-To: <20221216132215.27630-1-tdevries@suse.de>

On 12/16/22 14:22, Tom de Vries via Gdb-patches wrote:
> [ Partial resubmission of an earlier submission by Andrew (
> https://sourceware.org/pipermail/gdb-patches/2012-September/096347.html ), so
> listing him as co-author. ]
> 
> With x86_64-linux and target board unix/-m32, we have:
> ...
> (gdb) continue^M
> Continuing.^M
> Exception #10^M
> ^M
> Breakpoint 3, throw_exception_1 (e=10) at py-finish-breakpoint2.cc:23^M
> 23        throw new int (e);^M
> (gdb) FAIL: gdb.python/py-finish-breakpoint2.exp: \
>    check FinishBreakpoint in catch()
> ...
> 
> The following scenario happens:
> - set breakpoint in throw_exception_1, a function that throws an exception
> - continue
> - hit breakpoint, with call stack main.c:38 -> throw_exception_1
> - set a finish breakpoint
> - continue
> - hit the breakpoint again, with call stack main.c:48 -> throw_exception
>    -> throw_exception_1
> 
> Due to the exception, the function call did not properly terminate, and the
> finish breakpoint didn't trigger.  This is expected behaviour.
> 
> However, the intention is that gdb detects this situation at the next stop
> and calls the out_of_scope callback, which would result here in this test-case
> in a rather confusing "exception did not finish" message.  So the problem is
> that this message doesn't show up, in other words, the out_of_scope callback
> is not called.
> 
> [ Note that the fact that the situation is detected only at the next stop
> (wherever that happens to be could be) could be improved upon, and the earlier
> submission did that by setting a longjmp breakpoint.  But I'm considering this
> problem out-of-scope for this patch. ]
> 
> Note that the message does show up later, at thread exit:
> ...
> [Inferior 1 (process 20046) exited with code 0236]^M
> exception did not finish ...^M
> ...
> 
> The decision on whether to call the out_of_scope call back is taken in
> bpfinishpy_detect_out_scope_cb, and the interesting bit is here:
> ...
>               if (b->pspace == current_inferior ()->pspace
>                   && (!target_has_registers ()
>                       || frame_find_by_id (b->frame_id) == NULL))
>                 bpfinishpy_out_of_scope (finish_bp);
> ...
> 
> In the case of the thread exit, the callback triggers because
> target_has_registers () == 0.
> 
> So why doesn't the callback trigger in the case of the breakpoint?
> 
> Well, the b->frame_id is the frame_id of the frame of main (the frame
> in which the finish breakpoint is supposed to trigger), so AFAIU
> frame_find_by_id (b->frame_id) == NULL will only be true once we've
> left main, at which point I guess we don't stop till thread exit.
> 
> Fix this by saving the frame in which the finish breakpoint was created, and
> using frame_find_by_id () == NULL on that frame instead, such that we have:
> ...
> (gdb) continue^M
> Continuing.^M
> Exception #10^M
> ^M
> Breakpoint 3, throw_exception_1 (e=10) at py-finish-breakpoint2.cc:23^M
> 23        throw new int (e);^M
> exception did not finish ...^M
> (gdb) FAIL: gdb.python/py-finish-breakpoint2.exp: \
>    check FinishBreakpoint in catch()
> ...
> 
> Still, the test-case is failing because it's setup to match the behaviour that
> we get on x86_64-linux with target board unix/-m64:
> ...
> (gdb) continue^M
> Continuing.^M
> Exception #10^M
> stopped at ExceptionFinishBreakpoint^M
> (gdb) PASS: gdb.python/py-finish-breakpoint2.exp: \
>    check FinishBreakpoint in catch()
> ...
> 
> So what happens here?  Again, due to the exception, the function call did not
> properly terminate, but the finish breakpoint still triggers.  This is somewhat
> unexpected.  This happens because it just so happens to be that the frame
> return address at which the breakpoint is set, is also the first instruction
> after the exception has been handled.  This is a know problem, filed as
> PR29909, so KFAIL it, and modify the test-case to expect the out_of_scope
> callback.
> 
> Also add a breakpoint after setting the finish breakpoint but before throwing
> the exception, to check that we don't call the out_of_scope callback too early.
> 
> Tested on x86_64-linux, with target boards unix/-m32.
> 

Committed.

Thanks,
- Tom

> Co-Authored-By: Andrew Burgess <aburgess@redhat.com>
> PR python/27247
> Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=27247
> ---
>   gdb/python/py-finishbreakpoint.c              |  9 +++-
>   .../gdb.python/py-finish-breakpoint2.cc       |  5 +-
>   .../gdb.python/py-finish-breakpoint2.exp      | 47 +++++++++++++++++--
>   3 files changed, 55 insertions(+), 6 deletions(-)
> 
> diff --git a/gdb/python/py-finishbreakpoint.c b/gdb/python/py-finishbreakpoint.c
> index 0b5dbeb5413..ea6662f0a86 100644
> --- a/gdb/python/py-finishbreakpoint.c
> +++ b/gdb/python/py-finishbreakpoint.c
> @@ -55,6 +55,10 @@ 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;
>   };
>   
>   extern PyTypeObject finish_breakpoint_object_type
> @@ -310,6 +314,7 @@ bpfinishpy_init (PyObject *self, PyObject *args, PyObject *kwargs)
>   
>     self_bpfinish->py_bp.bp->frame_id = frame_id;
>     self_bpfinish->py_bp.is_finish_bp = 1;
> +  self_bpfinish->initiating_frame = get_frame_id (frame);
>   
>     /* Bind the breakpoint with the current program space.  */
>     self_bpfinish->py_bp.bp->pspace = current_program_space;
> @@ -360,9 +365,11 @@ bpfinishpy_detect_out_scope_cb (struct breakpoint *b,
>   	{
>   	  try
>   	    {
> +	      struct frame_id initiating_frame = 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 (initiating_frame) == NULL))
>   		bpfinishpy_out_of_scope (finish_bp);
>   	    }
>   	  catch (const gdb_exception &except)
> diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint2.cc b/gdb/testsuite/gdb.python/py-finish-breakpoint2.cc
> index 84234238f74..7e53d5393ae 100644
> --- a/gdb/testsuite/gdb.python/py-finish-breakpoint2.cc
> +++ b/gdb/testsuite/gdb.python/py-finish-breakpoint2.cc
> @@ -17,9 +17,13 @@
>   
>   #include <iostream>
>   
> +int i;
> +
>   void
>   throw_exception_1 (int e)
>   {
> +  i += 1; /* Finish breakpoint is set here.  */
> +  i += 1; /* Break before exception.  */
>     throw new int (e);
>   }
>   
> @@ -32,7 +36,6 @@ throw_exception (int e)
>   int
>   main (void)
>   {
> -  int i;
>     try
>       {
>         throw_exception_1 (10);
> diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint2.exp b/gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
> index 30758473d40..bd1e96bb4fe 100644
> --- a/gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
> +++ b/gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
> @@ -38,21 +38,60 @@ set pyfile [gdb_remote_download host \
>   # Check FinishBreakpoints against C++ exceptions
>   #
>   
> +gdb_breakpoint [gdb_get_line_number "Break before exception"]
>   gdb_breakpoint [gdb_get_line_number "Break after exception 2"]
>   
>   gdb_test "source $pyfile" ".*Python script imported.*" \
>            "import python scripts"
>            
>   gdb_breakpoint "throw_exception_1"
> +
> +#
> +# Part 1.
> +#
> +
>   gdb_test "continue" "Breakpoint .*throw_exception_1.*" "run to exception 1"
>   
> -gdb_test "python print (len(gdb.breakpoints()))" "3" "check BP count"
> +# Count breakpoints before setting finishbreakpoint.
> +gdb_test "python print (len(gdb.breakpoints()))" "4" "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 "python print (len(gdb.breakpoints()))" "3" "check finish BP removal"
>   
> -gdb_test "continue" ".*Breakpoint.* throw_exception_1.*" "continue to second exception"
> +gdb_test "continue" "Break before exception.*" \
> +    "break before exception"
> +
> +set need_continue 0
> +gdb_test_multiple "continue" "finishBP out-of-scope" {
> +    -re -wrap "exception did not finish.*" {
> +	# Out-of-scope.  For instance on x86_64 with unix/-m32.
> +	pass $gdb_test_name
> +    }
> +    -re -wrap "stopped at ExceptionFinishBreakpoint.*" {
> +	# Triggered despite the fact that the function call never finished.
> +	# It just so happens to be that the frame return address at which the
> +	# breakpoint is set, is also the first instruction after the exception
> +	# has been handled.  For instance on x86_64 with unix/-m64.
> +	kfail python/29909 $gdb_test_name
> +	set need_continue 1
> +    }
> +}
> +
> +# Count breakpoints, check that the finishbreakpoint has been removed.
> +gdb_test "python print (len(gdb.breakpoints()))" "4" "check finish BP removal"
> +
> +#
> +# Part 2.
> +#
> +
> +if { $need_continue } {
> +    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 again"
> +
> +gdb_test "continue" "Break before exception.*" \
> +    "break before exception again"
> +
>   gdb_test "continue" ".*exception did not finish.*" "FinishBreakpoint with exception thrown not caught"
> 
> base-commit: fa501b69309ccb03ec957101f24109ed7f737733

      reply	other threads:[~2022-12-31  8:10 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-12-16 13:22 [PATCH] " Tom de Vries
2022-12-31  8:10 ` Tom de Vries [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=0900fca3-4d52-f1d9-cbd6-90da923e2531@suse.de \
    --to=tdevries@suse.de \
    --cc=aburgess@redhat.com \
    --cc=gdb-patches@sourceware.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).