gdb: report specific stop reason for watchpoints

The remote protocol allows for implementations to report more specific
reasons for the break in execution back to the client [1]. This is
entirely optional, so it is only implemented for amd64, arm64, and i386
at the moment.

[1] https://sourceware.org/gdb/current/onlinedocs/gdb/Stop-Reply-Packets.html

Reviewed by:	jhb
MFC after:	3 weeks
Sponsored by:	NetApp, Inc.
Sponsored by:	Klara, Inc.
NetApp PR:	51
Differential Revision:	https://reviews.freebsd.org/D29174
This commit is contained in:
Mitchell Horne 2021-03-08 15:04:51 -04:00
parent 4beb385813
commit 7446b0888d
10 changed files with 108 additions and 3 deletions

View File

@ -49,6 +49,7 @@ __FBSDID("$FreeBSD$");
#include <machine/endian.h>
#include <gdb/gdb.h>
#include <gdb/gdb_int.h>
void *
gdb_cpu_getreg(int regnum, size_t *regsz)
@ -152,6 +153,40 @@ gdb_cpu_signal(int type, int code)
return (SIGEMT);
}
void
gdb_cpu_stop_reason(int type, int code)
{
uintmax_t val;
val = 0;
if (type == T_TRCTRAP) {
/* NB: 'code' contains the value of dr6 at the trap. */
if ((code & DBREG_DR6_B(0)) != 0) {
val = rdr0();
}
if ((code & DBREG_DR6_B(1)) != 0) {
val = rdr1();
}
if ((code & DBREG_DR6_B(2)) != 0) {
val = rdr2();
}
if ((code & DBREG_DR6_B(3)) != 0) {
val = rdr3();
}
/*
* TODO: validate the bits in DR7 to differentiate between a
* watchpoint trap and a hardware breakpoint trap (currently
* unsupported).
*/
if (val != 0) {
gdb_tx_str("watch:");
gdb_tx_varhex(val);
gdb_tx_char(';');
}
}
}
void *
gdb_begin_write(void)
{

View File

@ -72,5 +72,6 @@ void *gdb_cpu_getreg(int, size_t *);
void gdb_cpu_setreg(int, void *);
int gdb_cpu_signal(int, int);
void gdb_end_write(void *);
void gdb_cpu_stop_reason(int, int);
#endif /* !_MACHINE_GDB_MACHDEP_H_ */

View File

@ -66,6 +66,12 @@ gdb_end_write(void *arg __unused)
}
static __inline void
gdb_cpu_stop_reason(int type __unused, int code __unused)
{
}
void *gdb_cpu_getreg(int, size_t *);
void gdb_cpu_setreg(int, void *);
int gdb_cpu_signal(int, int);

View File

@ -41,6 +41,7 @@
#include <machine/pcb.h>
#include <gdb/gdb.h>
#include <gdb/gdb_int.h>
void *
gdb_cpu_getreg(int regnum, size_t *regsz)
@ -110,3 +111,14 @@ gdb_cpu_signal(int type, int code __unused)
}
return (SIGEMT);
}
void
gdb_cpu_stop_reason(int type, int code __unused)
{
if (type == EXCP_WATCHPT_EL1) {
gdb_tx_str("watch:");
gdb_tx_varhex((uintmax_t)READ_SPECIALREG(far_el1));
gdb_tx_char(';');
}
}

View File

@ -77,5 +77,6 @@ gdb_end_write(void *arg __unused)
void *gdb_cpu_getreg(int, size_t *);
void gdb_cpu_setreg(int, void *);
int gdb_cpu_signal(int, int);
void gdb_cpu_stop_reason(int, int);
#endif /* !_MACHINE_GDB_MACHDEP_H_ */

View File

@ -741,8 +741,9 @@ gdb_trap(int type, int code)
gdb_tx_char(':');
gdb_tx_reg(GDB_REG_PC);
gdb_tx_char(';');
gdb_cpu_stop_reason(type, code);
gdb_tx_str("thread:");
gdb_tx_varhex((long)kdb_thread->td_tid);
gdb_tx_varhex((uintmax_t)kdb_thread->td_tid);
gdb_tx_char(';');
gdb_tx_end(); /* XXX check error condition. */

View File

@ -36,13 +36,15 @@ __FBSDID("$FreeBSD$");
#include <sys/proc.h>
#include <sys/signal.h>
#include <machine/endian.h>
#include <machine/frame.h>
#include <machine/gdb_machdep.h>
#include <machine/pcb.h>
#include <machine/reg.h>
#include <machine/trap.h>
#include <machine/frame.h>
#include <machine/endian.h>
#include <gdb/gdb.h>
#include <gdb/gdb_int.h>
void *
gdb_cpu_getreg(int regnum, size_t *regsz)
@ -114,3 +116,37 @@ gdb_cpu_signal(int type, int code)
}
return (SIGEMT);
}
void
gdb_cpu_stop_reason(int type, int code)
{
uintmax_t val;
val = 0;
if (type == T_TRCTRAP) {
/* NB: 'code' contains the value of dr6 at the trap. */
if ((code & DBREG_DR6_B(0)) != 0) {
val = rdr0();
}
if ((code & DBREG_DR6_B(1)) != 0) {
val = rdr1();
}
if ((code & DBREG_DR6_B(2)) != 0) {
val = rdr2();
}
if ((code & DBREG_DR6_B(3)) != 0) {
val = rdr3();
}
/*
* TODO: validate the bits in DR7 to differentiate between a
* watchpoint trap and a hardware breakpoint trap (currently
* unsupported).
*/
if (val != 0) {
gdb_tx_str("watch:");
gdb_tx_varhex(val);
gdb_tx_char(';');
}
}
}

View File

@ -63,5 +63,6 @@ gdb_end_write(void *arg __unused)
void *gdb_cpu_getreg(int, size_t *);
void gdb_cpu_setreg(int, void *);
int gdb_cpu_signal(int, int);
void gdb_cpu_stop_reason(int, int);
#endif /* !_MACHINE_GDB_MACHDEP_H_ */

View File

@ -64,6 +64,12 @@ gdb_end_write(void *arg __unused)
}
static __inline void
gdb_cpu_stop_reason(int type __unused, int code __unused)
{
}
void *gdb_cpu_getreg(int, size_t *);
void gdb_cpu_setreg(int, void *);
int gdb_cpu_signal(int, int);

View File

@ -128,6 +128,12 @@ gdb_end_write(void *arg __unused)
}
static __inline void
gdb_cpu_stop_reason(int type __unused, int code __unused)
{
}
void *gdb_cpu_getreg(int, size_t *);
void gdb_cpu_setreg(int, void *);
int gdb_cpu_signal(int, int);