This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
Re: [PATCH 3/8] Deliver signal in hardware single step
- From: Pedro Alves <palves at redhat dot com>
- To: Yao Qi <qiyaoltc at gmail dot com>
- Cc: gdb-patches at sourceware dot org
- Date: Thu, 17 Mar 2016 12:12:53 +0000
- Subject: Re: [PATCH 3/8] Deliver signal in hardware single step
- Authentication-results: sourceware.org; auth=none
- References: <1457088276-1170-1-git-send-email-yao dot qi at linaro dot org> <1457088276-1170-4-git-send-email-yao dot qi at linaro dot org> <56E2A685 dot 2080602 at redhat dot com> <56E2A753 dot 9060805 at redhat dot com> <56E2AE04 dot 7030306 at redhat dot com> <86io0mya1n dot fsf at gmail dot com>
On 03/16/2016 10:47 AM, Yao Qi wrote:
> Pedro Alves <palves@redhat.com> writes:
>
> Hi Pedro,
>
>> - If there's a signal handler installed, we'll stop at its entry,
>> but we haven't stepped over the original breakpoint yet. If we
>> were already stopped at the entry of the signal handler, and it
>> nested, we'll find the program stopped at the same PC it had
>> started at. But we won't know whether the step-over finished
>> successfully (could be a "jmp $pc" instruction), or if instead we're
>> in a new handler invocation, and thus this is a new,
>> separate breakpoint hit.
>
> GDBserver doesn't have to tell the program stopped at the same PC
> it had started at when step over is finished. When step over, GDBserver
> uninsert breakpoint, resumes the lwp, and wait the next event from the
> lwp. Once the event from the lwp arrives, GDBserver reinsert breakpoint
> and thinks step over is finished. That is all ...
>
>>
>> A signal handler that segfaults in the first instruction
>> would be the easiest way to reproduce this that I can think of.
>> (If it's crashing, you'd expect the user might try putting a
>> breakpoint there.)
>
> I write a case like this, set the conditional breakpoint on the first
> instruction of handler, and looks breakpoint condition in target side
> still works properly.
>
> (gdb) disassemble handler
> Dump of assembler code for function handler:
> 0x0000000000400586 <+0>: movb $0x0,0x0
> 0x000000000040058e <+8>: retq
>
> (gdb) p/x $rsp
> $1 = 0x7fffffffde00
> (gdb) b *handler if $rsp < 0x7fffffffde00 - 3300
>
> the condition like this can guarantee that the condition will be true
> after several recursions.
>
>>
>> Now, if after stepping into the handler, we immediately reinsert the
>> breakpoint and continue, we'll retrap it, it ends up OK.
>
> breakpoint is reinserted after stepping into the handler, because
> GDBserver thinks step over is finished, as I said above.
>
>> But if we immediately instead start a new step-over for the same
>> breakpoint address, we will miss the new hit, and nest a new handler,
>> on and on.
>
> This won't happen. After hardware single step with signal delivered,
> the program stops at the entry of handler, and GDBserver gets an event.
> Then, GDBserver thinks step over is finished, and then proceed all lwps.
> However, the thread still stopped at the breakpoint (of different
> stack frames), so it needs step over again.
> The loop like this will go
> on until the breakpoint condition is true, need_step_over_p decides to
> resume rather than step over. The program will hit the breakpoint, and
> GDBserver reports the event back to GDB.
>
> Note in my experiment with the test case above, I don't let GDBserver
> treat SIGSEGV as SIGTRAP, otherwise SIGSEGV will be reported back to GDB.
>
OK.
We need to think of unconditional tracepoints as well,
not just conditional breakpoints, however.
Here's a likely scenario, that I think will happen:
#1 - tracepoint set at 0xf00.
#2 - Program stops at 0xf00, tracepoint is collected.
#3 - gdbserver starts a step-over
#4 - instead, a signal arrives, step-over cancelled.
#5 - signal should be passed, not reported to gdb.
#6 - gdbserver starts a new step-over, passing signal as well.
#7 - step lands in handler. step over finished.
#8 - gdbserver proceeds/continues
#9 - signal handler returns, again to 0xf00, tracepoint
traps again and is thus collected again. This is a
spurious collection.
gdb avoids the spurious double-hit in #9 by remembering that
it was stepping over a breakpoint when a signal arrived, in
order to continue the step over once the handler returns
(tp->step_after_step_resume_breakpoint). This only works if the handler
doesn't cause some other unrelated user-visible stop -- in that case,
then the next time the user continues the program, and the
handler returns to the original breakpoint address, we'll still
report a new breakpoint hit. I think.
Maybe we can just not bother and live with the spurious double
tracepoint hit/collect in gdbserver. (Likewise dprintf.)
Dunno, but I'm now starting to lean toward that.
Thanks,
Pedro Alves