Running the testsuite with a series that reimplements user-visible
all-stop behavior on top of a target running in non-stop mode revealed
problems related to event starvation avoidance.
For example, I see
gdb.threads/signal-while-stepping-over-bp-other-thread.exp failing.
What happens is that GDB core never gets to see the signal event. It
ends up processing the events for the same threads over an over,
because Linux's waitpid(-1, ...) returns that first task in the task
list that has an event, starving threads on the tail of the task list.
So I wrote a non-stop mode test originally inspired by
signal-while-stepping-over-bp-other-thread.exp, to stress this
independently of all-stop on top of non-stop. Fixing it required the
changes described below. The test will be added in a following
commit.
1) linux-nat.c has code in place that picks an event LWP at random out
of all that have had events. This is because on the kernel side,
"waitpid(-1, ...)" just walks the task list linearly looking for the
first that had an event. But, this code is currently only used in
all-stop mode. So with a multi-threaded program that has multiple
events triggering debug events in parallel, GDB ends up starving some
threads.
To make the event randomization work in non-stop mode too, the patch
makes us pull out all the already pending events on the kernel side,
with waitpid, before deciding which LWP to report to the core.
There's some code in linux_wait that takes care of leaving events
pending if they were for LWPs the caller is not interested in. The
patch moves that to linux_nat_filter_event, so that we only have one
place that leaves events pending. With that in place, conceptually,
the flow is simpler and more normalized:
#1 - walk the LWP list looking for an LWP with a pending event to report.
#2 - if no pending event, pull events out of the kernel, and store
them in the LWP structures as pending.
#3- goto #1.
2) Then, currently the event randomization code only considers SIGTRAP
(or trap-like) events. That means that if e.g., have have multiple
threads stepping in parallel that hit a breakpoint that needs stepping
over, and one gets a signal, the signal may end up never getting
processed, because GDB will always be giving priority to the SIGTRAPs.
The patch fixes this by making the randomization code consider all
kinds of pending events.
3) If multiple threads hit a breakpoint, we report one of those, and
"cancel" the others. Cancelling means decrementing the PC, and
discarding the event. If the next time the LWP is resumed the
breakpoint is still installed, the LWP should hit it again, and we'll
report the hit then. The problem I found is that this delays threads
from advancing too much, with the kernel potentially ending up
scheduling the same threads over and over, and others not advancing.
So the patch switches away from cancelling the breakpoints, and
instead remembering that the LWP had stopped for a breakpoint. If on
resume the breakpoint is still installed, we report it. If it's no
longer installed, we discard the pending event then. This is actually
how GDBserver used to handle this before d50171e4 (Teach linux
gdbserver to step-over-breakpoints), but with the difference that back
then we'd delay adjusting the PC until resuming, which made it so that
"info threads" could wrongly see threads with unadjusted PCs.
gdb/
2015-01-09 Pedro Alves <palves@redhat.com>
* breakpoint.c (hardware_breakpoint_inserted_here_p): New
function.
* breakpoint.h (hardware_breakpoint_inserted_here_p): New
declaration.
* linux-nat.c (linux_nat_status_is_event): Move higher up in file.
(linux_resume_one_lwp): Store the thread's PC. Adjust to clear
stop_reason.
(check_stopped_by_watchpoint): New function.
(save_sigtrap): Reimplement.
(linux_nat_stopped_by_watchpoint): Adjust.
(linux_nat_lp_status_is_event): Delete.
(stop_wait_callback): Only call save_sigtrap after storing the
pending status.
(status_callback): If the thread had been stopped for a breakpoint
that has since been removed, discard the event and resume the LWP.
(count_events_callback, select_event_lwp_callback): Use
lwp_status_pending_p instead of linux_nat_lp_status_is_event.
(cancel_breakpoint): Rename to ...
(check_stopped_by_breakpoint): ... this. Record whether the LWP
stopped for a software breakpoint or hardware breakpoint.
(select_event_lwp): Only give preference to the stepping LWP in
all-stop mode. Adjust comments.
(stop_and_resume_callback): Remove references to new_pending_p.
(linux_nat_filter_event): Likewise. Leave exit events of the
leader thread pending here. Handle signal short circuiting here.
Only call save_sigtrap after storing the pending waitstatus.
(linux_nat_wait_1): Remove 'retry' label. Remove references to
new_pending. Don't handle leaving events the caller is not
interested in pending here, nor handle signal short-circuiting
here. Also give equal priority to all LWPs that have had events
in non-stop mode. If reporting a software breakpoint event,
unadjust the LWP's PC.
* linux-nat.h (enum lwp_stop_reason): New.
(struct lwp_info) <stop_pc>: New field.
(struct lwp_info) <stopped_by_watchpoint>: Delete field.
(struct lwp_info) <stop_reason>: New field.
* x86-linux-nat.c (x86_linux_prepare_to_resume): Adjust.
The following patch...
commit 3116063bd6
Date: Fri Jun 27 09:52:29 2014 +0100
Subject: Tidy #include lists
... introduced a build failure on certain x86 GNU/Linux distributions
(reproduced on SuSE 10 and RHES4) due to "struct iovec" not being
defined. This struct is defined in <sys/uio.h>, which used to be
explicitly included, but no longer is after the commit above was
applied.
[...]/i386-linux-nat.c: In function 'fetch_xstateregs':
[...]/i386-linux-nat.c:325:16: error: storage size of 'iov' isn't known
[...]/i386-linux-nat.c: In function 'store_xstateregs':
[...]/i386-linux-nat.c:348:16: error: storage size of 'iov' isn't known
make[2]: *** [i386-linux-nat.o] Error 1
It seems to be working on newer GNU/Linux distros thanks to indirect
inclusion of <sys/uio.h>, but it does not work on some other versions
of the same distros. This is why indirect includes of public APIs
should be avoided if at all possible.
This patch fixes the issue by adding the explicit include back.
gdb/ChangeLog:
* i386-linux-nat.c, x86-linux-nat.c: Add <sys/uio.h> #include.
Historically the Linux x86 watchpoint code did not cope with multi-
threaded processes and LWP IDs were passed to it wrapped as PIDs.
Not all entry points were converted when the Linux x86 watchpoint
code was made multi-thread-aware, so a handler was left in place to
cope with wrapped LWPs. Since then all such entry points have been
converted to pass regular LWPs and the handler is now redundant.
This commit removes the handler and adds assertions to ensure no
wrapped LWPs are passed in future.
gdb/ChangeLog:
* x86-linux-nat.c (x86_linux_dr_get, x86_linux_dr_set):
Remove code to cope with LWPs wrapped as PIDs.
Add assertions to ensure no wrapped LWPs are passed.
The loop macro ALL_DEBUG_REGISTERS does not iterate over the status or
control registers, so its name is misleading. This commit renames it
as ALL_DEBUG_ADDRESS_REGISTERS and updates all uses. This commit also
updates its loop conditions to an equivalent but better form, and
makes two functions use it that had previously hardwired the loop.
A comment on a related field in the x86_debug_reg_state structure is
also updated to reflect that the field refers specifically to address
registers only.
gdb/ChangeLog:
* nat/x86-dregs.h (ALL_DEBUG_REGISTERS): Renamed as...
(ALL_DEBUG_ADDRESS_REGISTERS): New macro. All uses updated.
Loop conditions changed to equivalent form.
(struct x86_debug_reg_state): Updated dr_ref_count comment.
* x86-linux-nat.c (x86_linux_prepare_to_resume): Use
ALL_DEBUG_ADDRESS_REGISTERS.
gdb/gdbserver/ChangeLog:
* linux-x86-low.c (x86_linux_prepare_to_resume): Use
ALL_DEBUG_ADDRESS_REGISTERS.
This commit renames nine files that contain code used by both 32- and
64-bit Intel ports such that their names are prefixed with "x86"
rather than "i386". All types, functions and variables within these
files are likewise renamed such that their names are prefixed with
"x86" rather than "i386". This makes GDB follow the convention used
by gdbserver such that 32-bit Intel code lives in files called
"i386-*", 64-bit Intel code lives in files called "amd64-*", and code
for both 32- and 64-bit Intel lives in files called "x86-*".
This commit only renames OS-independent files. The Linux ports of
both GDB and gdbserver now follow the i386/amd64/x86 convention fully.
Some ports still use the old convention where "i386" in file/function/
type/variable names can mean "32-bit only" or "32- and 64-bit" but I
don't want to touch ports I can't fully test except where absolutely
necessary.
gdb/ChangeLog:
* i386-nat.h: Renamed as...
* x86-nat.h: New file. All type, function and variable name
prefixes changed from "i386_" to "x86_". All references updated.
* i386-nat.c: Renamed as...
* x86-nat.c: New file. All type, function and variable name
prefixes changed from "i386_" to "x86_". All references updated.
* common/i386-xstate.h: Renamed as...
* common/x86-xstate.h: New file. All type, function and variable
name prefixes changed from "i386_" to "x86_". All references
updated.
* nat/i386-cpuid.h: Renamed as...
* nat/x86-cpuid.h: New file. All type, function and variable name
prefixes changed from "i386_" to "x86_". All references updated.
* nat/i386-gcc-cpuid.h: Renamed as...
* nat/x86-gcc-cpuid.h: New file. All type, function and variable
name prefixes changed from "i386_" to "x86_". All references
updated.
* nat/i386-dregs.h: Renamed as...
* nat/x86-dregs.h: New file. All type, function and variable name
prefixes changed from "i386_" to "x86_". All references updated.
* nat/i386-dregs.c: Renamed as...
* nat/x86-dregs.c: New file. All type, function and variable name
prefixes changed from "i386_" to "x86_". All references updated.
gdb/gdbserver/ChangeLog:
* i386-low.h: Renamed as...
* x86-low.h: New file. All type, function and variable name
prefixes changed from "i386_" to "x86_". All references updated.
* i386-low.c: Renamed as...
* x86-low.c: New file. All type, function and variable name
prefixes changed from "i386_" to "x86_". All references updated.