[PATCHv4,Hurd] Add hardware watch support

Message ID 20140912182911.GF3202@type.youpi.perso.aquilenet.fr
State Committed
Headers

Commit Message

Samuel Thibault Sept. 12, 2014, 6:29 p.m. UTC
  2014-09-06  Samuel Thibault  <samuel.thibault@ens-lyon.org>

	Add hardware watch support to gnu-i386 platform.

        This allows to get the "watch" command keep native code execution.

	* gdb/gdb/gnu-nat.c (inf_threads): New function.
	* gdb/gdb/gnu-nat.h (inf_threads_ftype): New type.
	(inf_threads): New declaration.
	* gdb/gdb/i386gnu-nat.c: Include "x86-nat.h" and "inf-child.h".
	[i386_DEBUG_STATE] (i386_gnu_dr_get, i386_gnu_dr_set,
	i386_gnu_dr_set_control_one, i386_gnu_dr_set_control,
	i386_gnu_dr_set_addr_one, i386_gnu_dr_set_addr, i386_gnu_dr_get_reg,
	i386_gnu_dr_get_addr, 386_gnu_dr_get_status, i386_gnu_dr_get_control):
	New functions
	(reg_addr): New structure.
	(_initialize_i386gnu_nat) [i386_DEBUG_STATE]: Initialize hardware i386
	debugging register hooks.
  

Comments

Joel Brobecker Sept. 12, 2014, 7:59 p.m. UTC | #1
Hello Samuel,

On Fri, Sep 12, 2014 at 08:29:11PM +0200, Samuel Thibault wrote:
> 2014-09-06  Samuel Thibault  <samuel.thibault@ens-lyon.org>
> 
> 	Add hardware watch support to gnu-i386 platform.
> 
>         This allows to get the "watch" command keep native code execution.
> 
> 	* gdb/gdb/gnu-nat.c (inf_threads): New function.
> 	* gdb/gdb/gnu-nat.h (inf_threads_ftype): New type.
> 	(inf_threads): New declaration.
> 	* gdb/gdb/i386gnu-nat.c: Include "x86-nat.h" and "inf-child.h".
> 	[i386_DEBUG_STATE] (i386_gnu_dr_get, i386_gnu_dr_set,
> 	i386_gnu_dr_set_control_one, i386_gnu_dr_set_control,
> 	i386_gnu_dr_set_addr_one, i386_gnu_dr_set_addr, i386_gnu_dr_get_reg,
> 	i386_gnu_dr_get_addr, 386_gnu_dr_get_status, i386_gnu_dr_get_control):
> 	New functions
> 	(reg_addr): New structure.
> 	(_initialize_i386gnu_nat) [i386_DEBUG_STATE]: Initialize hardware i386
> 	debugging register hooks.

This is now OK. I double-checked that Samuel does have a copyright
assignment on file, so Thomas is good to go for pushing the patch in
if he approves as well.  If Sergio has any further comment on the patch,
we can address them as followups.

Thank you,
  
Sergio Durigan Junior Sept. 12, 2014, 8:01 p.m. UTC | #2
On Friday, September 12 2014, Joel Brobecker wrote:

> This is now OK. I double-checked that Samuel does have a copyright
> assignment on file, so Thomas is good to go for pushing the patch in
> if he approves as well.  If Sergio has any further comment on the patch,
> we can address them as followups.

It is OK by me, thanks Joel.
  

Patch

diff --git a/gdb/gnu-nat.c b/gdb/gnu-nat.c
index c8164d6..9d0957c 100644
--- a/gdb/gnu-nat.c
+++ b/gdb/gnu-nat.c
@@ -983,6 +983,17 @@  inf_port_to_thread (struct inf *inf, mach_port_t port)
   return 0;
 }
 
+/* See gnu-nat.h.  */
+
+void
+inf_threads (struct inf *inf, inf_threads_ftype *f, void *arg)
+{
+  struct proc *thread;
+
+  for (thread = inf->threads; thread; thread = thread->next)
+    f (thread, arg);
+}
+
 
 /* Make INF's list of threads be consistent with reality of TASK.  */
 void
diff --git a/gdb/gnu-nat.h b/gdb/gnu-nat.h
index 8e949eb..43d5b75 100644
--- a/gdb/gnu-nat.h
+++ b/gdb/gnu-nat.h
@@ -29,6 +29,11 @@  extern struct inf *gnu_current_inf;
 /* Converts a GDB pid to a struct proc.  */
 struct proc *inf_tid_to_thread (struct inf *inf, int tid);
 
+typedef void (inf_threads_ftype) (struct proc *thread, void *arg);
+
+/* Call F for every thread in inferior INF, passing ARG as second parameter.  */
+void inf_threads (struct inf *inf, inf_threads_ftype *f, void *arg);
+
 /* Makes sure that INF's thread list is synced with the actual process.  */
 int inf_update_procs (struct inf *inf);
 
diff --git a/gdb/i386gnu-nat.c b/gdb/i386gnu-nat.c
index 8fad871..acbe23e 100644
--- a/gdb/i386gnu-nat.c
+++ b/gdb/i386gnu-nat.c
@@ -18,6 +18,7 @@ 
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include "defs.h"
+#include "x86-nat.h"
 #include "inferior.h"
 #include "floatformat.h"
 #include "regcache.h"
@@ -30,6 +31,7 @@ 
 #include "i386-tdep.h"
 
 #include "gnu-nat.h"
+#include "inf-child.h"
 #include "i387-tdep.h"
 
 #ifdef HAVE_SYS_PROCFS_H
@@ -304,6 +306,142 @@  gnu_store_registers (struct target_ops *ops,
     }
 }
 
+
+/* Support for debug registers.  */
+
+#ifdef i386_DEBUG_STATE
+/* Get debug registers for thread THREAD.  */
+
+static void
+i386_gnu_dr_get (struct i386_debug_state *regs, struct proc *thread)
+{
+  mach_msg_type_number_t count = i386_DEBUG_STATE_COUNT;
+  error_t err;
+
+  err = thread_get_state (thread->port, i386_DEBUG_STATE,
+ 			  (thread_state_t) regs, &count);
+  if (err != 0 || count != i386_DEBUG_STATE_COUNT)
+    warning (_("Couldn't fetch debug state from %s"),
+	     proc_string (thread));
+}
+
+/* Set debug registers for thread THREAD.  */
+
+static void
+i386_gnu_dr_set (const struct i386_debug_state *regs, struct proc *thread)
+{
+  error_t err;
+
+  err = thread_set_state (thread->port, i386_DEBUG_STATE,
+			  (thread_state_t) regs, i386_DEBUG_STATE_COUNT);
+  if (err != 0)
+    warning (_("Couldn't store debug state into %s"),
+	     proc_string (thread));
+}
+
+/* Set DR_CONTROL in THREAD.  */
+
+static void
+i386_gnu_dr_set_control_one (struct proc *thread, void *arg)
+{
+  unsigned long *control = arg;
+  struct i386_debug_state regs;
+
+  i386_gnu_dr_get (&regs, thread);
+  regs.dr[DR_CONTROL] = *control;
+  i386_gnu_dr_set (&regs, thread);
+}
+
+/* Set DR_CONTROL to CONTROL in all threads.  */
+
+static void
+i386_gnu_dr_set_control (unsigned long control)
+{
+  inf_update_procs (gnu_current_inf);
+  inf_threads (gnu_current_inf, i386_gnu_dr_set_control_one, &control);
+}
+
+/* Parameters to set a debugging address.  */
+
+struct reg_addr
+{
+  int regnum;		/* Register number (zero based).  */
+  CORE_ADDR addr;	/* Address.  */
+};
+
+/* Set address REGNUM (zero based) to ADDR in THREAD.  */
+
+static void
+i386_gnu_dr_set_addr_one (struct proc *thread, void *arg)
+{
+  struct reg_addr *reg_addr = arg;
+  struct i386_debug_state regs;
+
+  i386_gnu_dr_get (&regs, thread);
+  regs.dr[reg_addr->regnum] = reg_addr->addr;
+  i386_gnu_dr_set (&regs, thread);
+}
+
+/* Set address REGNUM (zero based) to ADDR in all threads.  */
+
+static void
+i386_gnu_dr_set_addr (int regnum, CORE_ADDR addr)
+{
+  struct reg_addr reg_addr;
+
+  gdb_assert (DR_FIRSTADDR <= regnum && regnum <= DR_LASTADDR);
+
+  reg_addr.regnum = regnum;
+  reg_addr.addr = addr;
+
+  inf_update_procs (gnu_current_inf);
+  inf_threads (gnu_current_inf, i386_gnu_dr_set_addr_one, &reg_addr);
+}
+
+/* Get debug register REGNUM value from only the one LWP of PTID.  */
+
+static unsigned long
+i386_gnu_dr_get_reg (ptid_t ptid, int regnum)
+{
+  struct i386_debug_state regs;
+  struct proc *thread;
+
+  /* Make sure we know about new threads.  */
+  inf_update_procs (gnu_current_inf);
+
+  thread = inf_tid_to_thread (gnu_current_inf, ptid_get_lwp (ptid));
+  i386_gnu_dr_get (&regs, thread);
+
+  return regs.dr[regnum];
+}
+
+/* Return the inferior's debug register REGNUM.  */
+
+static CORE_ADDR
+i386_gnu_dr_get_addr (int regnum)
+{
+  gdb_assert (DR_FIRSTADDR <= regnum && regnum <= DR_LASTADDR);
+
+  return i386_gnu_dr_get_reg (inferior_ptid, regnum);
+}
+
+/* Get DR_STATUS from only the one thread of INFERIOR_PTID.  */
+
+static unsigned long
+i386_gnu_dr_get_status (void)
+{
+  return i386_gnu_dr_get_reg (inferior_ptid, DR_STATUS);
+}
+
+/* Return the inferior's DR7 debug control register.  */
+
+static unsigned long
+i386_gnu_dr_get_control (void)
+{
+  return i386_gnu_dr_get_reg (inferior_ptid, DR_CONTROL);
+}
+#endif /* i386_DEBUG_STATE */
+
 /* Provide a prototype to silence -Wmissing-prototypes.  */
 extern initialize_file_ftype _initialize_i386gnu_nat;
 
@@ -315,6 +453,18 @@  _initialize_i386gnu_nat (void)
   /* Fill in the generic GNU/Hurd methods.  */
   t = gnu_target ();
 
+#ifdef i386_DEBUG_STATE
+  x86_use_watchpoints (t);
+
+  x86_dr_low.set_control = i386_gnu_dr_set_control;
+  gdb_assert (DR_FIRSTADDR == 0 && DR_LASTADDR < i386_DEBUG_STATE_COUNT);
+  x86_dr_low.set_addr = i386_gnu_dr_set_addr;
+  x86_dr_low.get_addr = i386_gnu_dr_get_addr;
+  x86_dr_low.get_status = i386_gnu_dr_get_status;
+  x86_dr_low.get_control = i386_gnu_dr_get_control;
+  x86_set_debug_register_length (4);
+#endif /* i386_DEBUG_STATE */
+
   t->to_fetch_registers = gnu_fetch_registers;
   t->to_store_registers = gnu_store_registers;
 
diff --git a/gdb/config/i386/i386gnu.mh b/gdb/config/i386/i386gnu.mh
index a3ea122..4cc23e4 100644
--- a/gdb/config/i386/i386gnu.mh
+++ b/gdb/config/i386/i386gnu.mh
@@ -1,5 +1,6 @@ 
 # Host: Intel 386 running the GNU Hurd
-NATDEPFILES= i386gnu-nat.o gnu-nat.o core-regset.o fork-child.o \
+NATDEPFILES= i386gnu-nat.o gnu-nat.o \
+	     x86-nat.o x86-dregs.o core-regset.o fork-child.o \
 	     notify_S.o process_reply_S.o msg_reply_S.o \
 	     msg_U.o exc_request_U.o exc_request_S.o
 HAVE_NATIVE_GCORE_HOST = 1