|
|
01917d |
http://sourceware.org/ml/gdb-patches/2015-02/msg00730.html
|
|
|
01917d |
Subject: [PATCH 2/8] Teach GDB about targets that can tell whether a trap is a breakpoint event
|
|
|
01917d |
|
|
|
01917d |
The moribund locations heuristics are problematic. This patch teaches
|
|
|
01917d |
GDB about targets that can reliably tell whether a trap was caused by
|
|
|
01917d |
a software or hardware breakpoint, and thus don't need moribund
|
|
|
01917d |
locations, thus bypassing all the problems that mechanism has.
|
|
|
01917d |
|
|
|
01917d |
The non-stop-fair-events.exp test is frequently failing currently.
|
|
|
01917d |
E.g., see https://sourceware.org/ml/gdb-testers/2015-q1/msg03148.html.
|
|
|
01917d |
|
|
|
01917d |
The root cause is a fundamental problem with moribund locations. For
|
|
|
01917d |
example, the stepped_breakpoint logic added by af48d08f breaks in this
|
|
|
01917d |
case (which is what happens with that test):
|
|
|
01917d |
|
|
|
01917d |
- Step thread A, no breakpoint is set at PC.
|
|
|
01917d |
|
|
|
01917d |
- The kernel doesn't schedule thread A yet.
|
|
|
01917d |
|
|
|
01917d |
- Insert breakpoint at A's PC, for some reason (e.g., a step-resume
|
|
|
01917d |
breakpoint for thread B).
|
|
|
01917d |
|
|
|
01917d |
- Kernel finally schedules thread A.
|
|
|
01917d |
|
|
|
01917d |
- thread A's stepped_breakpoint flag is not set, even though it now
|
|
|
01917d |
stepped a breakpoint instruction.
|
|
|
01917d |
|
|
|
01917d |
- adjust_pc_after_break gets the PC wrong, because PC == PREV_PC, but
|
|
|
01917d |
stepped_breakpoint is not set.
|
|
|
01917d |
|
|
|
01917d |
We needed the stepped_breakpoint logic to workaround moribund
|
|
|
01917d |
locations, because otherwise adjust_pc_after_break could apply an
|
|
|
01917d |
adjustment when it shouldn't just because there _used_ to be a
|
|
|
01917d |
breakpoint at PC (a moribund breakpoint location). For example, on
|
|
|
01917d |
x86, that's wrong if the thread really hasn't executed an int3, but
|
|
|
01917d |
instead executed some other 1-byte long instruction. Getting the PC
|
|
|
01917d |
adjustment wrong of course leads to the inferior executing the wrong
|
|
|
01917d |
instruction.
|
|
|
01917d |
|
|
|
01917d |
Other problems with moribund locations are:
|
|
|
01917d |
|
|
|
01917d |
- if a true SIGTRAP happens to be raised when the program is
|
|
|
01917d |
executing the PC that used to have a breakpoint, GDB will assume
|
|
|
01917d |
that is a trap for a breakpoint that has recently been removed, and
|
|
|
01917d |
thus we miss reporting the random signal to the user.
|
|
|
01917d |
|
|
|
01917d |
- to minimize that, we get rid of moribund location after a while.
|
|
|
01917d |
That while is defined as just a certain number of events being
|
|
|
01917d |
processed. That number of events sometimes passes by before a
|
|
|
01917d |
delayed breakpoint is processed, and GDB confuses the trap for a
|
|
|
01917d |
random signal, thus reporting the random trap. Once the user
|
|
|
01917d |
resumes the thread, the program crashes because the PC was not
|
|
|
01917d |
adjusted...
|
|
|
01917d |
|
|
|
01917d |
The fix for all this is to bite the bullet and get rid of heuristics
|
|
|
01917d |
and instead rely on the target knowing accurately what caused the
|
|
|
01917d |
SIGTRAP. The target/kernel/stub is in the best position to know what
|
|
|
01917d |
that, because it can e.g. consult priviledged CPU flags GDB has no
|
|
|
01917d |
access to, or by knowing which exception vector entry was called when
|
|
|
01917d |
the instruction trapped, etc. Most debug APIs I've seen to date
|
|
|
01917d |
report breakpoint hits as a distinct event in some fashion. For
|
|
|
01917d |
example, on the Linux kernel, whether a breakpoint was executed is
|
|
|
01917d |
exposed to userspace in the si_code field of the SIGTRAP's siginfo.
|
|
|
01917d |
On Windows, the debug API reports a EXCEPTION_BREAKPOINT exception
|
|
|
01917d |
code.
|
|
|
01917d |
|
|
|
01917d |
We needed to keep around deleted breakpoints in an on-the-side list
|
|
|
01917d |
(the moribund locations) for two main reasons:
|
|
|
01917d |
|
|
|
01917d |
- Know that a SIGTRAP actually is a delayed event for a hit of a
|
|
|
01917d |
breakpoint that was removed before the event was processed, and
|
|
|
01917d |
thus should not be reported as a random signal.
|
|
|
01917d |
|
|
|
01917d |
- So we still do the decr_pc_after_break adjustment in that case, so
|
|
|
01917d |
that the thread is resumed at the correct address.
|
|
|
01917d |
|
|
|
01917d |
In the new model, if GDB processes an event the target tells is a
|
|
|
01917d |
breakpoint trap, and GDB doesn't find the corresponding breakpoint in
|
|
|
01917d |
its breakpoint tables, it means that event is a delayed event for a
|
|
|
01917d |
breakpoint that has since been removed, and thus the event should be
|
|
|
01917d |
ignored.
|
|
|
01917d |
|
|
|
01917d |
For the decr_pc_after_after issue, it ends up being much simpler that
|
|
|
01917d |
on targets that can reliably tell whether a breakpoint trapped, for
|
|
|
01917d |
the breakpoint trap to present the PC already adjusted. Proper
|
|
|
01917d |
multi-threading support already implies that targets needs to be doing
|
|
|
01917d |
decr_pc_after_break adjustment themselves, otherwise for example, in
|
|
|
01917d |
all-stop if two threads hit a breakpoint simultaneously, and the user
|
|
|
01917d |
does "info threads", he'll see the non-event thread that hit the
|
|
|
01917d |
breakpoint stopped at the wrong PC.
|
|
|
01917d |
|
|
|
01917d |
This way (target adjusts) also ends up eliminating the need for some
|
|
|
01917d |
awkward re-incrementing of the PC in the record-full and Linux targets
|
|
|
01917d |
that we do today, and the need for the target_decr_pc_after_break
|
|
|
01917d |
hook.
|
|
|
01917d |
|
|
|
01917d |
If the target always adjusts, then there's a case where GDB needs to
|
|
|
01917d |
re-increment the PC. Say, on x86, an "int3" instruction that was
|
|
|
01917d |
explicitly written in the program traps. In this case, GDB should
|
|
|
01917d |
report a random SIGTRAP signal to the user, with the PC pointing at
|
|
|
01917d |
the instruction past the int3, just like if GDB was not debugging the
|
|
|
01917d |
program. The user may well decide to pass the SIGTRAP to the program
|
|
|
01917d |
because the program being debugged has a SIGTRAP handler that handles
|
|
|
01917d |
its own breakpoints, and expects the PC to be unadjusted.
|
|
|
01917d |
|
|
|
01917d |
Tested on x86-64 Fedora 20.
|
|
|
01917d |
|
|
|
01917d |
gdb/ChangeLog:
|
|
|
01917d |
2015-02-25 Pedro Alves <palves@redhat.com>
|
|
|
01917d |
|
|
|
01917d |
* breakpoint.c (need_moribund_for_location_type): New function.
|
|
|
01917d |
(bpstat_stop_status): Don't skipping checking moribund locations
|
|
|
01917d |
of breakpoint types which the target tell caused a stop.
|
|
|
01917d |
(program_breakpoint_here_p): New function, factored out from ...
|
|
|
01917d |
(bp_loc_is_permanent): ... this.
|
|
|
01917d |
(update_global_location_list): Don't create a moribund location if
|
|
|
01917d |
the target supports reporting stops of the type of the removed
|
|
|
01917d |
breakpoint.
|
|
|
01917d |
* breakpoint.h (program_breakpoint_here_p): New declaration.
|
|
|
01917d |
* infrun.c (adjust_pc_after_break): Return early if the target has
|
|
|
01917d |
already adjusted the PC. Add comments.
|
|
|
01917d |
(handle_signal_stop): If nothing explains a signal, and the target
|
|
|
01917d |
tells us the stop was caused by a software breakpoint, check if
|
|
|
01917d |
there's a breakpoint instruction in the memory. If so, adjust the
|
|
|
01917d |
PC before presenting the stop to the user. Otherwise, ignore the
|
|
|
01917d |
trap. If nothing explains a signal, and the target tells us the
|
|
|
01917d |
stop was caused by a hardware breakpoint, ignore the trap.
|
|
|
01917d |
* target.h (struct target_ops)
|
|
|
01917d |
to_supports_stopped_by_sw_breakpoint, to_stopped_by_hw_breakpoint,
|
|
|
01917d |
to_supports_stopped_by_hw_breakpoint>: New fields.
|
|
|
01917d |
(target_stopped_by_sw_breakpoint)
|
|
|
01917d |
(target_supports_stopped_by_sw_breakpoint)
|
|
|
01917d |
(target_stopped_by_hw_breakpoint)
|
|
|
01917d |
(target_supports_stopped_by_hw_breakpoint): Define.
|
|
|
01917d |
* target-delegates.c: Regenerate.
|
|
|
01917d |
---
|
|
|
01917d |
gdb/breakpoint.c | 91 ++++++++++++++++++++++++------------
|
|
|
01917d |
gdb/breakpoint.h | 5 ++
|
|
|
01917d |
gdb/infrun.c | 70 +++++++++++++++++++++++++++-
|
|
|
01917d |
gdb/target-delegates.c | 124 +++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
01917d |
gdb/target.h | 44 ++++++++++++++++++
|
|
|
01917d |
5 files changed, 303 insertions(+), 31 deletions(-)
|
|
|
01917d |
|
|
|
01917d |
Index: gdb-7.6.1/gdb/target.h
|
|
|
01917d |
===================================================================
|
|
|
01917d |
--- gdb-7.6.1.orig/gdb/target.h 2016-03-13 22:01:58.833963789 +0100
|
|
|
01917d |
+++ gdb-7.6.1/gdb/target.h 2016-03-13 22:01:59.950971809 +0100
|
|
|
01917d |
@@ -462,6 +462,16 @@
|
|
|
01917d |
void (*to_files_info) (struct target_ops *);
|
|
|
01917d |
int (*to_insert_breakpoint) (struct gdbarch *, struct bp_target_info *);
|
|
|
01917d |
int (*to_remove_breakpoint) (struct gdbarch *, struct bp_target_info *);
|
|
|
01917d |
+
|
|
|
01917d |
+ /* Returns true if the target stopped for a hardware breakpoint.
|
|
|
01917d |
+ Likewise, if the target supports hardware breakpoints, this
|
|
|
01917d |
+ method is necessary for correct background execution / non-stop
|
|
|
01917d |
+ mode operation. Even though hardware breakpoints do not
|
|
|
01917d |
+ require PC adjustment, GDB needs to be able to tell whether the
|
|
|
01917d |
+ hardware breakpoint event is a delayed event for a breakpoint
|
|
|
01917d |
+ that is already gone and should thus be ignored. */
|
|
|
01917d |
+ int (*to_stopped_by_hw_breakpoint) (struct target_ops *);
|
|
|
01917d |
+
|
|
|
01917d |
int (*to_can_use_hw_breakpoint) (int, int, int);
|
|
|
01917d |
int (*to_ranged_break_num_registers) (struct target_ops *);
|
|
|
01917d |
int (*to_insert_hw_breakpoint) (struct gdbarch *, struct bp_target_info *);
|
|
|
01917d |
@@ -1548,6 +1558,9 @@
|
|
|
01917d |
#define target_stopped_by_watchpoint \
|
|
|
01917d |
(*current_target.to_stopped_by_watchpoint)
|
|
|
01917d |
|
|
|
01917d |
+#define target_stopped_by_hw_breakpoint() \
|
|
|
01917d |
+ ((*current_target.to_stopped_by_hw_breakpoint) (¤t_target))
|
|
|
01917d |
+
|
|
|
01917d |
/* Non-zero if we have steppable watchpoints */
|
|
|
01917d |
|
|
|
01917d |
#define target_have_steppable_watchpoint \
|
|
|
01917d |
Index: gdb-7.6.1/gdb/infrun.c
|
|
|
01917d |
===================================================================
|
|
|
01917d |
--- gdb-7.6.1.orig/gdb/infrun.c 2016-03-13 22:01:58.704962863 +0100
|
|
|
01917d |
+++ gdb-7.6.1/gdb/infrun.c 2016-03-13 22:17:33.735674697 +0100
|
|
|
01917d |
@@ -4295,6 +4295,18 @@
|
|
|
01917d |
ecs->event_thread->suspend.stop_signal = GDB_SIGNAL_TRAP;
|
|
|
01917d |
}
|
|
|
01917d |
|
|
|
01917d |
+ /* Maybe this was a trap for a hardware breakpoint/watchpoint that
|
|
|
01917d |
+ has since been removed. */
|
|
|
01917d |
+ if (ecs->random_signal && target_stopped_by_hw_breakpoint())
|
|
|
01917d |
+ {
|
|
|
01917d |
+ /* A delayed hardware breakpoint event. Ignore the trap. */
|
|
|
01917d |
+ if (debug_infrun)
|
|
|
01917d |
+ fprintf_unfiltered (gdb_stdlog,
|
|
|
01917d |
+ "infrun: delayed hardware breakpoint/watchpoint "
|
|
|
01917d |
+ "trap, ignoring\n");
|
|
|
01917d |
+ ecs->random_signal = 0;
|
|
|
01917d |
+ }
|
|
|
01917d |
+
|
|
|
01917d |
process_event_stop_test:
|
|
|
01917d |
|
|
|
01917d |
/* Re-fetch current thread's frame in case we did a
|
|
|
01917d |
Index: gdb-7.6.1/gdb/target.c
|
|
|
01917d |
===================================================================
|
|
|
01917d |
--- gdb-7.6.1.orig/gdb/target.c 2016-03-13 22:01:58.832963782 +0100
|
|
|
01917d |
+++ gdb-7.6.1/gdb/target.c 2016-03-13 22:17:20.367578754 +0100
|
|
|
01917d |
@@ -726,6 +726,7 @@
|
|
|
01917d |
/* Do not inherit to_memory_map. */
|
|
|
01917d |
/* Do not inherit to_flash_erase. */
|
|
|
01917d |
/* Do not inherit to_flash_done. */
|
|
|
01917d |
+ INHERIT (to_stopped_by_hw_breakpoint, t);
|
|
|
01917d |
}
|
|
|
01917d |
#undef INHERIT
|
|
|
01917d |
|
|
|
01917d |
@@ -968,6 +969,9 @@
|
|
|
01917d |
(int (*) (void))
|
|
|
01917d |
return_zero);
|
|
|
01917d |
de_fault (to_execution_direction, default_execution_direction);
|
|
|
01917d |
+ de_fault (to_stopped_by_hw_breakpoint,
|
|
|
01917d |
+ (int (*) (struct target_ops *))
|
|
|
01917d |
+ return_zero);
|
|
|
01917d |
|
|
|
01917d |
#undef de_fault
|
|
|
01917d |
|