diff options
author | Pedro Alves <palves@redhat.com> | 2015-03-04 20:41:16 +0000 |
---|---|---|
committer | Pedro Alves <palves@redhat.com> | 2015-03-04 20:41:16 +0000 |
commit | faf09f0119da40d9b408021ad5665a906e00ee59 (patch) | |
tree | c1cee6b85358a10f56b1279ab2864137665f26af /gdb/linux-nat.c | |
parent | f7e6eed5283bb5c8a3598dd986dc922b9a794f58 (diff) | |
download | gdb-faf09f0119da40d9b408021ad5665a906e00ee59.zip gdb-faf09f0119da40d9b408021ad5665a906e00ee59.tar.gz gdb-faf09f0119da40d9b408021ad5665a906e00ee59.tar.bz2 |
Linux native: Use TRAP_BRKPT/TRAP_HWBPT
This patch adjusts the native Linux target backend to tell the core
whether a trap was caused by a breakpoint.
It teaches the target to get that information out of the si_code of
the SIGTRAP siginfo.
Tested on x86-64 Fedora 20, s390 RHEL 7, and PPC64 Fedora 18. An
earlier version was tested on ARM Fedora 21.
gdb/ChangeLog:
2015-03-04 Pedro Alves <palves@redhat.com>
* linux-nat.c (save_sigtrap): Check for breakpoints before
checking watchpoints.
(status_callback) [USE_SIGTRAP_SIGINFO]: Don't check whether a
breakpoint is inserted if relying on SIGTRAP's siginfo.si_code.
(check_stopped_by_breakpoint) [USE_SIGTRAP_SIGINFO]: Decide whether
a breakpoint triggered based on the SIGTRAP's siginfo.si_code.
(linux_nat_stopped_by_sw_breakpoint)
(linux_nat_supports_stopped_by_sw_breakpoint)
(linux_nat_stopped_by_hw_breakpoint)
(linux_nat_supports_stopped_by_hw_breakpoint): New functions.
(linux_nat_wait_1): Don't re-increment the PC if relying on
SIGTRAP's siginfo->si_code.
(linux_nat_add_target): Install new target methods.
* linux-thread-db.c (check_event): Don't account for breakpoint PC
offset if the target already adjusted the PC.
* nat/linux-ptrace.h (USE_SIGTRAP_SIGINFO): New.
(GDB_ARCH_TRAP_BRKPT): New.
(TRAP_HWBKPT): Define if not already defined.
Diffstat (limited to 'gdb/linux-nat.c')
-rw-r--r-- | gdb/linux-nat.c | 109 |
1 files changed, 104 insertions, 5 deletions
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c index 09edaf9..af77df2 100644 --- a/gdb/linux-nat.c +++ b/gdb/linux-nat.c @@ -2399,11 +2399,19 @@ save_sigtrap (struct lwp_info *lp) gdb_assert (lp->stop_reason == TARGET_STOPPED_BY_NO_REASON); gdb_assert (lp->status != 0); - if (check_stopped_by_watchpoint (lp)) - return; - + /* Check first if this was a SW/HW breakpoint before checking + watchpoints, because at least s390 can't tell the data address of + hardware watchpoint hits, and the kernel returns + stopped-by-watchpoint as long as there's a watchpoint set. */ if (linux_nat_status_is_event (lp->status)) check_stopped_by_breakpoint (lp); + + /* Note that TRAP_HWBKPT can indicate either a hardware breakpoint + or hardware watchpoint. Check which is which if we got + TARGET_STOPPED_BY_HW_BREAKPOINT. */ + if (lp->stop_reason == TARGET_STOPPED_BY_NO_REASON + || lp->stop_reason == TARGET_STOPPED_BY_HW_BREAKPOINT) + check_stopped_by_watchpoint (lp); } /* Returns true if the LWP had stopped for a watchpoint. */ @@ -2557,6 +2565,8 @@ status_callback (struct lwp_info *lp, void *data) paddress (target_gdbarch (), pc)); discard = 1; } + +#if !USE_SIGTRAP_SIGINFO else if (!breakpoint_inserted_here_p (get_regcache_aspace (regcache), pc)) { if (debug_linux_nat) @@ -2567,6 +2577,7 @@ status_callback (struct lwp_info *lp, void *data) discard = 1; } +#endif if (discard) { @@ -2669,10 +2680,49 @@ check_stopped_by_breakpoint (struct lwp_info *lp) struct gdbarch *gdbarch = get_regcache_arch (regcache); CORE_ADDR pc; CORE_ADDR sw_bp_pc; +#if USE_SIGTRAP_SIGINFO + siginfo_t siginfo; +#endif pc = regcache_read_pc (regcache); sw_bp_pc = pc - target_decr_pc_after_break (gdbarch); +#if USE_SIGTRAP_SIGINFO + if (linux_nat_get_siginfo (lp->ptid, &siginfo)) + { + if (siginfo.si_signo == SIGTRAP) + { + if (siginfo.si_code == GDB_ARCH_TRAP_BRKPT) + { + if (debug_linux_nat) + fprintf_unfiltered (gdb_stdlog, + "CSBB: Push back software " + "breakpoint for %s\n", + target_pid_to_str (lp->ptid)); + + /* Back up the PC if necessary. */ + if (pc != sw_bp_pc) + regcache_write_pc (regcache, sw_bp_pc); + + lp->stop_pc = sw_bp_pc; + lp->stop_reason = TARGET_STOPPED_BY_SW_BREAKPOINT; + return 1; + } + else if (siginfo.si_code == TRAP_HWBKPT) + { + if (debug_linux_nat) + fprintf_unfiltered (gdb_stdlog, + "CSBB: Push back hardware " + "breakpoint/watchpoint for %s\n", + target_pid_to_str (lp->ptid)); + + lp->stop_pc = pc; + lp->stop_reason = TARGET_STOPPED_BY_HW_BREAKPOINT; + return 1; + } + } + } +#else if ((!lp->step || lp->stop_pc == sw_bp_pc) && software_breakpoint_inserted_here_p (get_regcache_aspace (regcache), sw_bp_pc)) @@ -2704,10 +2754,53 @@ check_stopped_by_breakpoint (struct lwp_info *lp) lp->stop_reason = TARGET_STOPPED_BY_HW_BREAKPOINT; return 1; } +#endif return 0; } + +/* Returns true if the LWP had stopped for a software breakpoint. */ + +static int +linux_nat_stopped_by_sw_breakpoint (struct target_ops *ops) +{ + struct lwp_info *lp = find_lwp_pid (inferior_ptid); + + gdb_assert (lp != NULL); + + return lp->stop_reason == TARGET_STOPPED_BY_SW_BREAKPOINT; +} + +/* Implement the supports_stopped_by_sw_breakpoint method. */ + +static int +linux_nat_supports_stopped_by_sw_breakpoint (struct target_ops *ops) +{ + return USE_SIGTRAP_SIGINFO; +} + +/* Returns true if the LWP had stopped for a hardware + breakpoint/watchpoint. */ + +static int +linux_nat_stopped_by_hw_breakpoint (struct target_ops *ops) +{ + struct lwp_info *lp = find_lwp_pid (inferior_ptid); + + gdb_assert (lp != NULL); + + return lp->stop_reason == TARGET_STOPPED_BY_HW_BREAKPOINT; +} + +/* Implement the supports_stopped_by_hw_breakpoint method. */ + +static int +linux_nat_supports_stopped_by_hw_breakpoint (struct target_ops *ops) +{ + return USE_SIGTRAP_SIGINFO; +} + /* Select one LWP out of those that have events pending. */ static void @@ -3360,8 +3453,10 @@ linux_nat_wait_1 (struct target_ops *ops, gdb_assert (lp != NULL); /* Now that we've selected our final event LWP, un-adjust its PC if - it was a software breakpoint. */ - if (lp->stop_reason == TARGET_STOPPED_BY_SW_BREAKPOINT) + it was a software breakpoint, and we can't reliably support the + "stopped by software breakpoint" stop reason. */ + if (lp->stop_reason == TARGET_STOPPED_BY_SW_BREAKPOINT + && !USE_SIGTRAP_SIGINFO) { struct regcache *regcache = get_thread_regcache (lp->ptid); struct gdbarch *gdbarch = get_regcache_arch (regcache); @@ -4651,6 +4746,10 @@ linux_nat_add_target (struct target_ops *t) t->to_thread_address_space = linux_nat_thread_address_space; t->to_stopped_by_watchpoint = linux_nat_stopped_by_watchpoint; t->to_stopped_data_address = linux_nat_stopped_data_address; + t->to_stopped_by_sw_breakpoint = linux_nat_stopped_by_sw_breakpoint; + t->to_supports_stopped_by_sw_breakpoint = linux_nat_supports_stopped_by_sw_breakpoint; + t->to_stopped_by_hw_breakpoint = linux_nat_stopped_by_hw_breakpoint; + t->to_supports_stopped_by_hw_breakpoint = linux_nat_supports_stopped_by_hw_breakpoint; t->to_can_async_p = linux_nat_can_async_p; t->to_is_async_p = linux_nat_is_async_p; |