2014-09-06 Samuel Thibault samuel.thiba...@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.
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