public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
From: Andrew Burgess <aburgess@redhat.com>
To: Simon Marchi <simon.marchi@polymtl.ca>, gdb-patches@sourceware.org
Cc: pedro@palves.net
Subject: Re: [PATCH 2/2] gdb: MI stopped events when unwindonsignal is on
Date: Fri, 08 Sep 2023 09:54:31 +0100	[thread overview]
Message-ID: <8734zpm4e0.fsf@redhat.com> (raw)
In-Reply-To: <73807dfe-139f-431f-928c-ba7695cfc1cf@polymtl.ca>

Simon Marchi <simon.marchi@polymtl.ca> writes:

> On 8/8/23 10:45, Andrew Burgess via Gdb-patches wrote:
>> This recent commit:
>> 
>>   commit b1c0ab20809a502b2d2224fecb0dca3ada2e9b22
>>   Date:   Wed Jul 12 21:56:50 2023 +0100
>> 
>>       gdb: avoid double stop after failed breakpoint condition check
>> 
>> Addressed a problem where two MI stopped events would be reported if a
>> breakpoint condition failed due to a signal, this commit was a
>> replacement for this commit:
>> 
>>   commit 2e411b8c68eb2b035b31d5b00d940d4be1a0928b
>>   Date:   Fri Oct 14 14:53:15 2022 +0100
>> 
>>       gdb: don't always print breakpoint location after failed condition check
>> 
>> which solved the two stop problem, but only for the CLI.  Before both
>> of these commits, if a b/p condition failed due to a signal then the
>> user would see two stops reported, the first at the location where the
>> signal occurred, and the second at the location of the breakpoint.
>> 
>> By default GDB remains at the location where the signal occurred, so
>> the second reported stop can be confusing, this is the problem that
>> commit 2e411b8c68eb tried to solve (for the CLI) and b1c0ab20809a
>> extended also address the issue for MI too.
>> 
>> However, while working on another patch I realised that there was a
>> problem with GDB after the above commits.  Neither of the above
>> commits considered 'set unwindonsignal on'.  With this setting on,
>> when an inferior function call fails with a signal GDB will unwind the
>> stack back to the location where the inferior function call started.
>> In the b/p case we're looking at, the stop should be reported at the
>> location of the breakpoint, not at the location where the signal
>> occurred, and this isn't what happens.
>> 
>> This commit fixes this by ensuring that when unwindonsignal is 'on',
>> GDB reports a single stop event at the location of the breakpoint,
>> this fixes things for both CLI and MI.
>> 
>> The function call_thread_fsm::should_notify_stop is called when the
>> inferior function call completes and GDB is figuring out if the user
>> should be notified about this stop event by calling normal_stop from
>> fetch_inferior_event in infrun.c.  If normal_stop is called, then this
>> notification will be for the location where the inferior call stopped,
>> which will be the location at which the signal occurred.
>> 
>> Prior to this commit, the only time that normal_stop was not called,
>> was if the inferior function call completed successfully, this was
>> controlled by ::should_notify_stop, which only turns false when the
>> inferior function call has completed successfully.
>> 
>> In this commit I have extended the logic in ::should_notify_stop.  Now
>> there are three cases in which ::should_notify_stop will return false,
>> and we will not announce the first stop (by calling normal_stop).
>> These three reasons are:
>> 
>>   1. If the inferior function call completes successfully, this is
>>   unchanged behaviour,
>> 
>>   2. If the inferior function call stopped due to a signal and 'set
>>   unwindonsignal on' is in effect, and
>> 
>>   3. If the inferior function call stopped due to an uncaught C++
>>   exception, and 'set unwind-on-terminating-exception on' is in
>>   effect.
>> 
>> However, if we don't call normal_stop then we need to call
>> async_enable_stdin in call_thread_fsm::should_stop.  Prior to this
>> commit this was only done for the case where the inferior function
>> call completed successfully.
>> 
>> In this commit I now call ::should_notify_stop and use this to
>> determine if we need to call async_enable_stdin.  With this done we
>> now call async_enable_stdin for each of the three cases listed above,
>> which means that GDB will exit wait_sync_command_done correctly (see
>> run_inferior_call in infcall.c).
>> 
>> With these two changes the problem is mostly resolved.  However, the
>> solution isn't ideal, we've still lost some information.
>> 
>> Here is how GDB 13.1 behaves, this is before commits b1c0ab20809a and
>> 2e411b8c68eb:
>> 
>>   $ gdb -q /tmp/mi-condbreak-fail \
>>         -ex 'set unwindonsignal on' \
>>         -ex 'break 30 if (cond_fail())' \
>>         -ex 'run'
>>   Reading symbols from /tmp/mi-condbreak-fail...
>>   Breakpoint 1 at 0x40111e: file /tmp/mi-condbreak-fail.c, line 30.
>>   Starting program: /tmp/mi-condbreak-fail
>> 
>>   Program received signal SIGSEGV, Segmentation fault.
>>   0x0000000000401116 in cond_fail () at /tmp/mi-condbreak-fail.c:24
>>   24	  return *p;			/* Crash here.  */
>>   Error in testing breakpoint condition:
>>   The program being debugged was signaled while in a function called from GDB.
>>   GDB has restored the context to what it was before the call.
>>   To change this behavior use "set unwindonsignal off".
>>   Evaluation of the expression containing the function
>>   (cond_fail) will be abandoned.
>> 
>>   Breakpoint 1, foo () at /tmp/mi-condbreak-fail.c:30
>>   30	  global_counter += 1;		/* Set breakpoint here.  */
>>   (gdb)
>> 
>> In this state we see two stop notifications, the first is where the
>> signal occurred, while the second is where the breakpoint is located.
>> As GDB has unwound the stack (thanks to unwindonsignal) the second
>> stop notification reflects where the inferior is actually located.
>> 
>> Then after commits b1c0ab20809a and 2e411b8c68eb the behaviour changed
>> to this:
>> 
>>   $ gdb -q /tmp/mi-condbreak-fail \
>>         -ex 'set unwindonsignal on' \
>> 	-ex 'break 30 if (cond_fail())' \
>> 	-ex 'run'
>>   Reading symbols from /tmp/mi-condbreak-fail...
>>   Breakpoint 1 at 0x40111e: file /tmp/mi-condbreak-fail.c, line 30.
>>   Starting program: /tmp/mi-condbreak-fail
>> 
>>   Program received signal SIGSEGV, Segmentation fault.
>>   0x0000000000401116 in cond_fail () at /tmp/mi-condbreak-fail.c:24
>>   24	  return *p;			/* Crash here.  */
>>   Error in testing condition for breakpoint 1:
>>   The program being debugged was signaled while in a function called from GDB.
>>   GDB has restored the context to what it was before the call.
>>   To change this behavior use "set unwindonsignal off".
>>   Evaluation of the expression containing the function
>>   (cond_fail) will be abandoned.
>>   (gdb) bt 1
>>   #0  foo () at /tmp/mi-condbreak-fail.c:30
>>   (More stack frames follow...)
>>   (gdb)
>> 
>> This is the broken state.  GDB is reports the SIGSEGV location, but
>> not the unwound breakpoint location.  The final 'bt 1' shows that the
>> inferior is not located in cond_fail, which is the only location GDB
>> reported, so this is clearly wrong.
>> 
>> After implementing the fixes described above we now get this
>> behaviour:
>> 
>>   $ gdb -q /tmp/mi-condbreak-fail \
>>         -ex 'set unwindonsignal on' \
>>         -ex 'break 30 if (cond_fail())' \
>>         -ex 'run'
>>   Reading symbols from /tmp/mi-condbreak-fail...
>>   Breakpoint 1 at 0x40111e: file /tmp/mi-condbreak-fail.c, line 30.
>>   Starting program: /tmp/mi-condbreak-fail
>>   Error in testing breakpoint condition for breakpoint 1:
>>   The program being debugged was signaled while in a function called from GDB.
>>   GDB has restored the context to what it was before the call.
>>   To change this behavior use "set unwindonsignal off".
>>   Evaluation of the expression containing the function
>>   (cond_fail) will be abandoned.
>> 
>>   Breakpoint 1, foo () at /tmp/mi-condbreak-fail.c:30
>>   30	  global_counter += 1;		/* Set breakpoint here.  */
>>   (gdb)
>> 
>> This is better.  GDB now reports a single stop at the location of the
>> breakpoint, which is where the inferior is actually located.  However,
>> by removing the first stop notification we have lost some potentially
>> useful information about which signal caused the inferior to stop.
>> 
>> To address this I've reworked the message that is printed to include
>> the signal information.  GDB now reports this:
>> 
>>   $ gdb -q /tmp/mi-condbreak-fail \
>>         -ex 'set unwindonsignal on' \
>>         -ex 'break 30 if (cond_fail())' \
>>         -ex 'run'
>>   Reading symbols from /tmp/mi-condbreak-fail...
>>   Breakpoint 1 at 0x40111e: file /tmp/mi-condbreak-fail.c, line 30.
>>   Starting program: /tmp/mi-condbreak-fail
>>   Error in testing condition for breakpoint 1:
>>   The program being debugged received signal SIGSEGV, Segmentation fault
>>   while in a function called from GDB.  GDB has restored the context
>>   to what it was before the call.  To change this behavior use
>>   "set unwindonsignal off".  Evaluation of the expression containing
>>   the function (cond_fail) will be abandoned.
>> 
>>   Breakpoint 1, foo () at /tmp/mi-condbreak-fail.c:30
>>   30	  global_counter += 1;		/* Set breakpoint here.  */
>>   (gdb)
>> 
>> This is better, the user now sees a single stop notification at the
>> correct location, and the error message describes which signal caused
>> the inferior function call to stop.
>> 
>> However, we have lost the information about where the signal
>> occurred.  I did consider trying to include this information in the
>> error message, but, in the end, I opted not too.  I wasn't sure it was
>> worth the effort.  If the user has selected to unwind on signal, then
>> surely this implies they maybe aren't interested in debugging failed
>> inferior calls, so, hopefully, just knowing the signal name will be
>> enough.  I figure we can always add this information in later if
>> there's a demand for it.
>
> Hi Andrew,
>
> Starting with this commit, I see this failure with the native-gdbserver
> board:
>
> FAIL: gdb.mi/mi-condbreak-throw.exp: unwind_on_exception=off: wait for stop (unexpected output)
>
> Do you see it?

Hi Simon,

I've been away for a while, but I'm now back and will take a look at
this regression.

Thanks,
Andrew


  reply	other threads:[~2023-09-08  8:54 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-08-08 14:45 [PATCH 0/2] Stop notifications " Andrew Burgess
2023-08-08 14:45 ` [PATCH 1/2] gdb/testsuite: add mi_info_frame helper proc (and use it) Andrew Burgess
2023-08-08 14:45 ` [PATCH 2/2] gdb: MI stopped events when unwindonsignal is on Andrew Burgess
2023-08-10 15:06   ` Thiago Jung Bauermann
2023-08-14 14:50     ` Andrew Burgess
2023-08-15  0:32       ` Thiago Jung Bauermann
2023-08-16 14:28         ` Andrew Burgess
2023-08-18 21:56           ` Thiago Jung Bauermann
2023-08-23  9:33             ` Andrew Burgess
2023-08-29 16:37   ` Simon Marchi
2023-09-08  8:54     ` Andrew Burgess [this message]
2023-09-08 10:03     ` Andrew Burgess

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=8734zpm4e0.fsf@redhat.com \
    --to=aburgess@redhat.com \
    --cc=gdb-patches@sourceware.org \
    --cc=pedro@palves.net \
    --cc=simon.marchi@polymtl.ca \
    /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).