Index: gdb/i386-linux-nat.c |
diff --git a/gdb/i386-linux-nat.c b/gdb/i386-linux-nat.c |
index 5a65ef6a2f33c6e3093478c9d8ada944ece75ef5..c2f4fcc87005863b134196a9155d8b021d1295da 100644 |
--- a/gdb/i386-linux-nat.c |
+++ b/gdb/i386-linux-nat.c |
@@ -1,6 +1,6 @@ |
/* Native-dependent code for GNU/Linux i386. |
- Copyright (C) 1999-2012 Free Software Foundation, Inc. |
+ Copyright (C) 1999-2013 Free Software Foundation, Inc. |
This file is part of GDB. |
@@ -25,9 +25,11 @@ |
#include "regset.h" |
#include "target.h" |
#include "linux-nat.h" |
+#include "linux-btrace.h" |
+#include "btrace.h" |
#include "gdb_assert.h" |
-#include "gdb_string.h" |
+#include <string.h> |
#include "elf/common.h" |
#include <sys/uio.h> |
#include <sys/ptrace.h> |
@@ -100,7 +102,7 @@ static int have_ptrace_getregset = -1; |
(I386_ST0_REGNUM <= (regno) && (regno) < I386_SSE_NUM_REGS) |
#define GETXSTATEREGS_SUPPLIES(regno) \ |
- (I386_ST0_REGNUM <= (regno) && (regno) < I386_AVX_NUM_REGS) |
+ (I386_ST0_REGNUM <= (regno) && (regno) < I386_MPX_NUM_REGS) |
/* Does the current host support the GETREGS request? */ |
int have_ptrace_getregs = |
@@ -146,9 +148,9 @@ fetch_register (struct regcache *regcache, int regno) |
} |
/* GNU/Linux LWP ID's are process ID's. */ |
- tid = TIDGET (inferior_ptid); |
+ tid = ptid_get_lwp (inferior_ptid); |
if (tid == 0) |
- tid = PIDGET (inferior_ptid); /* Not a threaded program. */ |
+ tid = ptid_get_pid (inferior_ptid); /* Not a threaded program. */ |
errno = 0; |
val = ptrace (PTRACE_PEEKUSER, tid, |
@@ -174,9 +176,9 @@ store_register (const struct regcache *regcache, int regno) |
return; |
/* GNU/Linux LWP ID's are process ID's. */ |
- tid = TIDGET (inferior_ptid); |
+ tid = ptid_get_lwp (inferior_ptid); |
if (tid == 0) |
- tid = PIDGET (inferior_ptid); /* Not a threaded program. */ |
+ tid = ptid_get_pid (inferior_ptid); /* Not a threaded program. */ |
errno = 0; |
regcache_raw_collect (regcache, regno, &val); |
@@ -512,9 +514,9 @@ i386_linux_fetch_inferior_registers (struct target_ops *ops, |
} |
/* GNU/Linux LWP ID's are process ID's. */ |
- tid = TIDGET (inferior_ptid); |
+ tid = ptid_get_lwp (inferior_ptid); |
if (tid == 0) |
- tid = PIDGET (inferior_ptid); /* Not a threaded program. */ |
+ tid = ptid_get_pid (inferior_ptid); /* Not a threaded program. */ |
/* Use the PTRACE_GETFPXREGS request whenever possible, since it |
transfers more registers in one system call, and we'll cache the |
@@ -593,9 +595,9 @@ i386_linux_store_inferior_registers (struct target_ops *ops, |
} |
/* GNU/Linux LWP ID's are process ID's. */ |
- tid = TIDGET (inferior_ptid); |
+ tid = ptid_get_lwp (inferior_ptid); |
if (tid == 0) |
- tid = PIDGET (inferior_ptid); /* Not a threaded program. */ |
+ tid = ptid_get_pid (inferior_ptid); /* Not a threaded program. */ |
/* Use the PTRACE_SETFPXREGS requests whenever possible, since it |
transfers more registers in one system call. But remember that |
@@ -650,9 +652,9 @@ i386_linux_dr_get (ptid_t ptid, int regnum) |
int tid; |
unsigned long value; |
- tid = TIDGET (ptid); |
+ tid = ptid_get_lwp (ptid); |
if (tid == 0) |
- tid = PIDGET (ptid); |
+ tid = ptid_get_pid (ptid); |
errno = 0; |
value = ptrace (PTRACE_PEEKUSER, tid, |
@@ -670,9 +672,9 @@ i386_linux_dr_set (ptid_t ptid, int regnum, unsigned long value) |
{ |
int tid; |
- tid = TIDGET (ptid); |
+ tid = ptid_get_lwp (ptid); |
if (tid == 0) |
- tid = PIDGET (ptid); |
+ tid = ptid_get_pid (ptid); |
errno = 0; |
ptrace (PTRACE_POKEUSER, tid, |
@@ -708,8 +710,8 @@ i386_linux_dr_get_status (void) |
return i386_linux_dr_get (inferior_ptid, DR_STATUS); |
} |
-/* Callback for linux_nat_iterate_watchpoint_lwps. Update the debug registers |
- of LWP. */ |
+/* Callback for iterate_over_lwps. Update the debug registers of |
+ LWP. */ |
static int |
update_debug_registers_callback (struct lwp_info *lwp, void *arg) |
@@ -735,7 +737,9 @@ update_debug_registers_callback (struct lwp_info *lwp, void *arg) |
static void |
i386_linux_dr_set_control (unsigned long control) |
{ |
- linux_nat_iterate_watchpoint_lwps (update_debug_registers_callback, NULL); |
+ ptid_t pid_ptid = pid_to_ptid (ptid_get_pid (inferior_ptid)); |
+ |
+ iterate_over_lwps (pid_ptid, update_debug_registers_callback, NULL); |
} |
/* Set address REGNUM (zero based) to ADDR in all LWPs of the current |
@@ -748,7 +752,7 @@ i386_linux_dr_set_addr (int regnum, CORE_ADDR addr) |
gdb_assert (regnum >= 0 && regnum <= DR_LASTADDR - DR_FIRSTADDR); |
- linux_nat_iterate_watchpoint_lwps (update_debug_registers_callback, NULL); |
+ iterate_over_lwps (pid_ptid, update_debug_registers_callback, NULL); |
} |
/* Called when resuming a thread. |
@@ -767,7 +771,8 @@ i386_linux_prepare_to_resume (struct lwp_info *lwp) |
if (lwp->arch_private->debug_registers_changed) |
{ |
- struct i386_debug_reg_state *state = i386_debug_reg_state (); |
+ struct i386_debug_reg_state *state |
+ = i386_debug_reg_state (ptid_get_pid (lwp->ptid)); |
int i; |
/* See amd64_linux_prepare_to_resume for Linux kernel note on |
@@ -803,6 +808,41 @@ i386_linux_new_thread (struct lwp_info *lp) |
lp->arch_private = info; |
} |
+ |
+/* linux_nat_new_fork hook. */ |
+ |
+static void |
+i386_linux_new_fork (struct lwp_info *parent, pid_t child_pid) |
+{ |
+ pid_t parent_pid; |
+ struct i386_debug_reg_state *parent_state; |
+ struct i386_debug_reg_state *child_state; |
+ |
+ /* NULL means no watchpoint has ever been set in the parent. In |
+ that case, there's nothing to do. */ |
+ if (parent->arch_private == NULL) |
+ return; |
+ |
+ /* Linux kernel before 2.6.33 commit |
+ 72f674d203cd230426437cdcf7dd6f681dad8b0d |
+ will inherit hardware debug registers from parent |
+ on fork/vfork/clone. Newer Linux kernels create such tasks with |
+ zeroed debug registers. |
+ |
+ GDB core assumes the child inherits the watchpoints/hw |
+ breakpoints of the parent, and will remove them all from the |
+ forked off process. Copy the debug registers mirrors into the |
+ new process so that all breakpoints and watchpoints can be |
+ removed together. The debug registers mirror will become zeroed |
+ in the end before detaching the forked off process, thus making |
+ this compatible with older Linux kernels too. */ |
+ |
+ parent_pid = ptid_get_pid (parent->ptid); |
+ parent_state = i386_debug_reg_state (parent_pid); |
+ child_state = i386_debug_reg_state (child_pid); |
+ *child_state = *parent_state; |
+} |
+ |
/* Called by libthread_db. Returns a pointer to the thread local |
@@ -877,7 +917,7 @@ static void |
i386_linux_resume (struct target_ops *ops, |
ptid_t ptid, int step, enum gdb_signal signal) |
{ |
- int pid = PIDGET (ptid); |
+ int pid = ptid_get_pid (ptid); |
int request; |
@@ -960,9 +1000,9 @@ i386_linux_read_description (struct target_ops *ops) |
static uint64_t xcr0; |
/* GNU/Linux LWP ID's are process ID's. */ |
- tid = TIDGET (inferior_ptid); |
+ tid = ptid_get_lwp (inferior_ptid); |
if (tid == 0) |
- tid = PIDGET (inferior_ptid); /* Not a threaded program. */ |
+ tid = ptid_get_pid (inferior_ptid); /* Not a threaded program. */ |
#ifdef HAVE_PTRACE_GETFPXREGS |
if (have_ptrace_getfpxregs == -1) |
@@ -1001,13 +1041,64 @@ i386_linux_read_description (struct target_ops *ops) |
} |
/* Check the native XCR0 only if PTRACE_GETREGSET is available. */ |
- if (have_ptrace_getregset |
- && (xcr0 & I386_XSTATE_AVX_MASK) == I386_XSTATE_AVX_MASK) |
- return tdesc_i386_avx_linux; |
+ if (have_ptrace_getregset) |
+ { |
+ switch ((xcr0 & I386_XSTATE_ALL_MASK)) |
+ { |
+ case I386_XSTATE_MPX_MASK: |
+ return tdesc_i386_mpx_linux; |
+ case I386_XSTATE_AVX_MASK: |
+ return tdesc_i386_avx_linux; |
+ default: |
+ return tdesc_i386_linux; |
+ } |
+ } |
else |
return tdesc_i386_linux; |
} |
+/* Enable branch tracing. */ |
+ |
+static struct btrace_target_info * |
+i386_linux_enable_btrace (ptid_t ptid) |
+{ |
+ struct btrace_target_info *tinfo; |
+ struct gdbarch *gdbarch; |
+ |
+ errno = 0; |
+ tinfo = linux_enable_btrace (ptid); |
+ |
+ if (tinfo == NULL) |
+ error (_("Could not enable branch tracing for %s: %s."), |
+ target_pid_to_str (ptid), safe_strerror (errno)); |
+ |
+ /* Fill in the size of a pointer in bits. */ |
+ gdbarch = target_thread_architecture (ptid); |
+ tinfo->ptr_bits = gdbarch_ptr_bit (gdbarch); |
+ |
+ return tinfo; |
+} |
+ |
+/* Disable branch tracing. */ |
+ |
+static void |
+i386_linux_disable_btrace (struct btrace_target_info *tinfo) |
+{ |
+ int errcode = linux_disable_btrace (tinfo); |
+ |
+ if (errcode != 0) |
+ error (_("Could not disable branch tracing: %s."), safe_strerror (errcode)); |
+} |
+ |
+/* Teardown branch tracing. */ |
+ |
+static void |
+i386_linux_teardown_btrace (struct btrace_target_info *tinfo) |
+{ |
+ /* Ignore errors. */ |
+ linux_disable_btrace (tinfo); |
+} |
+ |
/* -Wmissing-prototypes */ |
extern initialize_file_ftype _initialize_i386_linux_nat; |
@@ -1041,8 +1132,17 @@ _initialize_i386_linux_nat (void) |
t->to_read_description = i386_linux_read_description; |
+ /* Add btrace methods. */ |
+ t->to_supports_btrace = linux_supports_btrace; |
+ t->to_enable_btrace = i386_linux_enable_btrace; |
+ t->to_disable_btrace = i386_linux_disable_btrace; |
+ t->to_teardown_btrace = i386_linux_teardown_btrace; |
+ t->to_read_btrace = linux_read_btrace; |
+ |
/* Register the target. */ |
linux_nat_add_target (t); |
linux_nat_set_new_thread (t, i386_linux_new_thread); |
+ linux_nat_set_new_fork (t, i386_linux_new_fork); |
+ linux_nat_set_forget_process (t, i386_forget_process); |
linux_nat_set_prepare_to_resume (t, i386_linux_prepare_to_resume); |
} |