+2007-09-30 Daniel Jacobowitz <dan@codesourcery.com>
+
+ * linux-nat.c (linux_nat_new_thread): New variable.
+ (linux_child_follow_fork): Set inferior_ptid to include LWP ID. Use
+ linux_nat_switch_fork.
+ (lwp_list): Make public.
+ (add_lwp): Call linux_nat_new_thread.
+ (lin_lwp_attach_lwp, linux_nat_attach): Call add_lwp after stopping
+ the new thread.
+ (resume_callback): Clear lp->siginfo. Remove unused variable.
+ (linux_nat_resume): Assert that the LWP list is already initialized.
+ Clear lp->siginfo.
+ (save_siginfo): New.
+ (stop_wait_callback, linux_nat_wait): Call it.
+ (linux_nat_set_new_thread, linux_nat_get_siginfo): New.
+ * linux-nat.h (struct lwp_info): Add siginfo.
+ (lwp_list, linux_nat_set_new_thread, linux_nat_get_siginfo): Declare.
+ (ALL_LWPS): Define.
+
+ * amd64-linux-nat.c (amd64_linux_dr): New.
+ (amd64_linux_dr_get): Take a PTID argument. Correct typo.
+ (amd64_linux_dr_set): Take a PTID argument.
+ (amd64_linux_dr_set_control, amd64_linux_dr_set_addr): Use ALL_LWPS.
+ (amd64_linux_dr_reset_addr): Use amd64_linux_dr_set_addr.
+ (amd64_linux_dr_get_status): Pass inferior_ptid to amd64_linux_dr_get.
+ (amd64_linux_new_thread): New.
+ (_initialize_amd64_linux_nat): Call linux_nat_set_new_thread.
+ * i386-linux-nat.c (i386_linux_dr): New.
+ (i386_linux_dr_get, i386_linux_dr_set): Take a PTID argument.
+ (i386_linux_dr_set_control, i386_linux_dr_set_addr): Use ALL_LWPS.
+ (i386_linux_dr_reset_addr): Use i386_linux_dr_set_addr.
+ (i386_linux_dr_get_status): Pass inferior_ptid to i386_linux_dr_get.
+ (i386_linux_new_thread): New.
+ (i386_linux_resume): Remove unnecessary PID check.
+ (_initialize_i386_linux_nat): Call linux_nat_set_new_thread.
+ * ia64-linux-nat.c (enable_watchpoints_in_psr): Take PTID argument.
+ (fetch_debug_register, fetch_debug_register_pair): Delete.
+ (debug_registers): New.
+ (ia64_linux_insert_watchpoint, ia64_linux_remove_watchpoint): Use
+ ALL_LWPS and debug_registers.
+ (ia64_linux_new_thread): New.
+ (ia64_linux_stopped_data_address): Use linux_nat_get_siginfo.
+ (_initialize_ia64_linux_nat): Call linux_nat_set_new_thread.
+ * ppc-linux-nat.c (last_stopped_data_address): Delete.
+ (saved_dabr_value): New.
+ (ppc_linux_insert_watchpoint, ppc_linux_remove_watchpoint): Use
+ ALL_LWPS.
+ (ppc_linux_new_thread): New.
+ (ppc_linux_stopped_data_address): Use linux_nat_get_siginfo.
+ (ppc_linux_stopped_by_watchpoint): Call ppc_linux_stopped_data_address.
+ (_initialize_ppc_linux_nat): Call linux_nat_set_new_thread.
+ * s390-nat.c (s390_stopped_by_watchpoint): Clear the watchpoint status
+ after reading it.
+ (s390_fix_watch_points): Take a PTID argument.
+ (s390_insert_watchpoint, s390_remove_watchpoint): Use ALL_LWPS.
+ (_initialize_s390_nat): Call linux_nat_set_new_thread.
+
2007-09-30 Daniel Jacobowitz <dan@codesourcery.com>
Jeff Johnston <jjohnstn@redhat.com>
}
}
\f
+/* Support for debug registers. */
+
+static unsigned long amd64_linux_dr[DR_CONTROL + 1];
static unsigned long
-amd64_linux_dr_get (int regnum)
+amd64_linux_dr_get (ptid_t ptid, int regnum)
{
int tid;
unsigned long value;
- /* FIXME: kettenis/2001-01-29: It's not clear what we should do with
- multi-threaded processes here. For now, pretend there is just
- one thread. */
- tid = PIDGET (inferior_ptid);
+ tid = TIDGET (ptid);
+ if (tid == 0)
+ tid = PIDGET (ptid);
/* FIXME: kettenis/2001-03-27: Calling perror_with_name if the
ptrace call fails breaks debugging remote targets. The correct
way to fix this is to add the hardware breakpoint and watchpoint
- stuff to the target vectore. For now, just return zero if the
+ stuff to the target vector. For now, just return zero if the
ptrace call fails. */
errno = 0;
- value = ptrace (PT_READ_U, tid,
+ value = ptrace (PTRACE_PEEKUSER, tid,
offsetof (struct user, u_debugreg[regnum]), 0);
if (errno != 0)
#if 0
}
static void
-amd64_linux_dr_set (int regnum, unsigned long value)
+amd64_linux_dr_set (ptid_t ptid, int regnum, unsigned long value)
{
int tid;
- /* FIXME: kettenis/2001-01-29: It's not clear what we should do with
- multi-threaded processes here. For now, pretend there is just
- one thread. */
- tid = PIDGET (inferior_ptid);
+ tid = TIDGET (ptid);
+ if (tid == 0)
+ tid = PIDGET (ptid);
errno = 0;
- ptrace (PT_WRITE_U, tid, offsetof (struct user, u_debugreg[regnum]), value);
+ ptrace (PTRACE_POKEUSER, tid,
+ offsetof (struct user, u_debugreg[regnum]), value);
if (errno != 0)
perror_with_name (_("Couldn't write debug register"));
}
void
amd64_linux_dr_set_control (unsigned long control)
{
- amd64_linux_dr_set (DR_CONTROL, control);
+ struct lwp_info *lp;
+ ptid_t ptid;
+
+ amd64_linux_dr[DR_CONTROL] = control;
+ ALL_LWPS (lp, ptid)
+ amd64_linux_dr_set (ptid, DR_CONTROL, control);
}
void
amd64_linux_dr_set_addr (int regnum, CORE_ADDR addr)
{
+ struct lwp_info *lp;
+ ptid_t ptid;
+
gdb_assert (regnum >= 0 && regnum <= DR_LASTADDR - DR_FIRSTADDR);
- amd64_linux_dr_set (DR_FIRSTADDR + regnum, addr);
+ amd64_linux_dr[DR_FIRSTADDR + regnum] = addr;
+ ALL_LWPS (lp, ptid)
+ amd64_linux_dr_set (ptid, DR_FIRSTADDR + regnum, addr);
}
void
amd64_linux_dr_reset_addr (int regnum)
{
- gdb_assert (regnum >= 0 && regnum <= DR_LASTADDR - DR_FIRSTADDR);
-
- amd64_linux_dr_set (DR_FIRSTADDR + regnum, 0L);
+ amd64_linux_dr_set_addr (regnum, 0);
}
unsigned long
amd64_linux_dr_get_status (void)
{
- return amd64_linux_dr_get (DR_STATUS);
+ return amd64_linux_dr_get (inferior_ptid, DR_STATUS);
+}
+
+static void
+amd64_linux_new_thread (ptid_t ptid)
+{
+ int i;
+
+ for (i = DR_FIRSTADDR; i <= DR_LASTADDR; i++)
+ amd64_linux_dr_set (ptid, i, amd64_linux_dr[i]);
+
+ amd64_linux_dr_set (ptid, DR_CONTROL, amd64_linux_dr[DR_CONTROL]);
}
\f
/* Register the target. */
linux_nat_add_target (t);
+ linux_nat_set_new_thread (t, amd64_linux_new_thread);
}
/* Support for debug registers. */
+static unsigned long i386_linux_dr[DR_CONTROL + 1];
+
static unsigned long
-i386_linux_dr_get (int regnum)
+i386_linux_dr_get (ptid_t ptid, int regnum)
{
int tid;
unsigned long value;
- /* FIXME: kettenis/2001-01-29: It's not clear what we should do with
- multi-threaded processes here. For now, pretend there is just
- one thread. */
- tid = PIDGET (inferior_ptid);
+ tid = TIDGET (ptid);
+ if (tid == 0)
+ tid = PIDGET (ptid);
/* FIXME: kettenis/2001-03-27: Calling perror_with_name if the
ptrace call fails breaks debugging remote targets. The correct
}
static void
-i386_linux_dr_set (int regnum, unsigned long value)
+i386_linux_dr_set (ptid_t ptid, int regnum, unsigned long value)
{
int tid;
- /* FIXME: kettenis/2001-01-29: It's not clear what we should do with
- multi-threaded processes here. For now, pretend there is just
- one thread. */
- tid = PIDGET (inferior_ptid);
+ tid = TIDGET (ptid);
+ if (tid == 0)
+ tid = PIDGET (ptid);
errno = 0;
ptrace (PTRACE_POKEUSER, tid,
void
i386_linux_dr_set_control (unsigned long control)
{
- i386_linux_dr_set (DR_CONTROL, control);
+ struct lwp_info *lp;
+ ptid_t ptid;
+
+ i386_linux_dr[DR_CONTROL] = control;
+ ALL_LWPS (lp, ptid)
+ i386_linux_dr_set (ptid, DR_CONTROL, control);
}
void
i386_linux_dr_set_addr (int regnum, CORE_ADDR addr)
{
+ struct lwp_info *lp;
+ ptid_t ptid;
+
gdb_assert (regnum >= 0 && regnum <= DR_LASTADDR - DR_FIRSTADDR);
- i386_linux_dr_set (DR_FIRSTADDR + regnum, addr);
+ i386_linux_dr[DR_FIRSTADDR + regnum] = addr;
+ ALL_LWPS (lp, ptid)
+ i386_linux_dr_set (ptid, DR_FIRSTADDR + regnum, addr);
}
void
i386_linux_dr_reset_addr (int regnum)
{
- gdb_assert (regnum >= 0 && regnum <= DR_LASTADDR - DR_FIRSTADDR);
-
- i386_linux_dr_set (DR_FIRSTADDR + regnum, 0L);
+ i386_linux_dr_set_addr (regnum, 0);
}
unsigned long
i386_linux_dr_get_status (void)
{
- return i386_linux_dr_get (DR_STATUS);
+ return i386_linux_dr_get (inferior_ptid, DR_STATUS);
+}
+
+static void
+i386_linux_new_thread (ptid_t ptid)
+{
+ int i;
+
+ for (i = DR_FIRSTADDR; i <= DR_LASTADDR; i++)
+ i386_linux_dr_set (ptid, i, i386_linux_dr[i]);
+
+ i386_linux_dr_set (ptid, DR_CONTROL, i386_linux_dr[DR_CONTROL]);
}
\f
int request = PTRACE_CONT;
- if (pid == -1)
- /* Resume all threads. */
- /* I think this only gets used in the non-threaded case, where "resume
- all threads" and "resume inferior_ptid" are the same. */
- pid = PIDGET (inferior_ptid);
-
if (step)
{
struct regcache *regcache = get_thread_regcache (pid_to_ptid (pid));
/* Register the target. */
linux_nat_add_target (t);
+ linux_nat_set_new_thread (t, i386_linux_new_thread);
}
#define IA64_PSR_DD (1UL << 39)
static void
-enable_watchpoints_in_psr (struct regcache *regcache)
+enable_watchpoints_in_psr (ptid_t ptid)
{
+ struct regcache *regcache = get_thread_regcache (ptid);
ULONGEST psr;
regcache_cooked_read_unsigned (regcache, IA64_PSR_REGNUM, &psr);
}
}
-static long
-fetch_debug_register (ptid_t ptid, int idx)
-{
- long val;
- int tid;
-
- tid = TIDGET (ptid);
- if (tid == 0)
- tid = PIDGET (ptid);
-
- val = ptrace (PT_READ_U, tid, (PTRACE_TYPE_ARG3) (PT_DBR + 8 * idx), 0);
-
- return val;
-}
+static long debug_registers[8];
static void
store_debug_register (ptid_t ptid, int idx, long val)
(void) ptrace (PT_WRITE_U, tid, (PTRACE_TYPE_ARG3) (PT_DBR + 8 * idx), val);
}
-static void
-fetch_debug_register_pair (ptid_t ptid, int idx, long *dbr_addr, long *dbr_mask)
-{
- if (dbr_addr)
- *dbr_addr = fetch_debug_register (ptid, 2 * idx);
- if (dbr_mask)
- *dbr_mask = fetch_debug_register (ptid, 2 * idx + 1);
-}
-
static void
store_debug_register_pair (ptid_t ptid, int idx, long *dbr_addr, long *dbr_mask)
{
static int
ia64_linux_insert_watchpoint (CORE_ADDR addr, int len, int rw)
{
- ptid_t ptid = inferior_ptid;
+ struct lwp_info *lp;
+ ptid_t ptid;
int idx;
long dbr_addr, dbr_mask;
int max_watchpoints = 4;
for (idx = 0; idx < max_watchpoints; idx++)
{
- fetch_debug_register_pair (ptid, idx, NULL, &dbr_mask);
+ dbr_mask = debug_registers[idx * 2 + 1];
if ((dbr_mask & (0x3UL << 62)) == 0)
{
/* Exit loop if both r and w bits clear */
return -1;
}
- store_debug_register_pair (ptid, idx, &dbr_addr, &dbr_mask);
- enable_watchpoints_in_psr (get_current_regcache ());
+ debug_registers[2 * idx] = dbr_addr;
+ debug_registers[2 * idx + 1] = dbr_mask;
+ ALL_LWPS (lp, ptid)
+ {
+ store_debug_register_pair (ptid, idx, &dbr_addr, &dbr_mask);
+ enable_watchpoints_in_psr (ptid);
+ }
return 0;
}
static int
ia64_linux_remove_watchpoint (CORE_ADDR addr, int len, int type)
{
- ptid_t ptid = inferior_ptid;
int idx;
long dbr_addr, dbr_mask;
int max_watchpoints = 4;
for (idx = 0; idx < max_watchpoints; idx++)
{
- fetch_debug_register_pair (ptid, idx, &dbr_addr, &dbr_mask);
+ dbr_addr = debug_registers[2 * idx];
+ dbr_mask = debug_registers[2 * idx + 1];
if ((dbr_mask & (0x3UL << 62)) && addr == (CORE_ADDR) dbr_addr)
{
+ struct lwp_info *lp;
+ ptid_t ptid;
+
+ debug_registers[2 * idx] = 0;
+ debug_registers[2 * idx + 1] = 0;
dbr_addr = 0;
dbr_mask = 0;
- store_debug_register_pair (ptid, idx, &dbr_addr, &dbr_mask);
+
+ ALL_LWPS (lp, ptid)
+ store_debug_register_pair (ptid, idx, &dbr_addr, &dbr_mask);
+
return 0;
}
}
return -1;
}
+static void
+ia64_linux_new_thread (ptid_t ptid)
+{
+ int i, any;
+
+ any = 0;
+ for (i = 0; i < 8; i++)
+ {
+ if (debug_registers[i] != 0)
+ any = 1;
+ store_debug_register (ptid, i, debug_registers[i]);
+ }
+
+ if (any)
+ enable_watchpoints_in_psr (ptid);
+}
+
static int
ia64_linux_stopped_data_address (struct target_ops *ops, CORE_ADDR *addr_p)
{
CORE_ADDR psr;
- int tid;
- struct siginfo siginfo;
- ptid_t ptid = inferior_ptid;
+ struct siginfo *siginfo_p;
struct regcache *regcache = get_current_regcache ();
- tid = TIDGET(ptid);
- if (tid == 0)
- tid = PIDGET (ptid);
-
- errno = 0;
- ptrace (PTRACE_GETSIGINFO, tid, (PTRACE_TYPE_ARG3) 0, &siginfo);
+ siginfo_p = linux_nat_get_siginfo (inferior_ptid);
- if (errno != 0 || siginfo.si_signo != SIGTRAP ||
- (siginfo.si_code & 0xffff) != 0x0004 /* TRAP_HWBKPT */)
+ if (siginfo_p->si_signo != SIGTRAP
+ || (siginfo_p->si_code & 0xffff) != 0x0004 /* TRAP_HWBKPT */)
return 0;
regcache_cooked_read_unsigned (regcache, IA64_PSR_REGNUM, &psr);
for the next instruction */
regcache_cooked_write_unsigned (regcache, IA64_PSR_REGNUM, psr);
- *addr_p = (CORE_ADDR)siginfo.si_addr;
+ *addr_p = (CORE_ADDR)siginfo_p->si_addr;
return 1;
}
/* Register the target. */
linux_nat_add_target (t);
+ linux_nat_set_new_thread (t, ia64_linux_new_thread);
}
static struct target_ops *linux_ops;
static struct target_ops linux_ops_saved;
+/* The method to call, if any, when a new thread is attached. */
+static void (*linux_nat_new_thread) (ptid_t);
+
/* The saved to_xfer_partial method, inherited from inf-ptrace.c.
Called by our to_xfer_partial. */
static LONGEST (*super_xfer_partial) (struct target_ops *,
target_detach (NULL, 0);
}
- inferior_ptid = pid_to_ptid (child_pid);
+ inferior_ptid = ptid_build (child_pid, child_pid, 0);
/* Reinstall ourselves, since we might have been removed in
target_detach (which does other necessary cleanup). */
push_target (ops);
+ linux_nat_switch_fork (inferior_ptid);
/* Reset breakpoints in the child as appropriate. */
follow_inferior_reset_breakpoints ();
because the "zombies" stay around. */
/* List of known LWPs. */
-static struct lwp_info *lwp_list;
+struct lwp_info *lwp_list;
/* Number of LWPs in the list. */
static int num_lwps;
}
/* Add the LWP specified by PID to the list. Return a pointer to the
- structure describing the new LWP. */
+ structure describing the new LWP. The LWP should already be stopped
+ (with an exception for the very first LWP). */
static struct lwp_info *
add_lwp (ptid_t ptid)
lwp_list = lp;
++num_lwps;
+ if (num_lwps > 1 && linux_nat_new_thread != NULL)
+ linux_nat_new_thread (ptid);
+
return lp;
}
{
pid_t pid;
int status;
+ int cloned = 0;
if (ptrace (PTRACE_ATTACH, GET_LWP (ptid), 0, 0) < 0)
{
return -1;
}
- if (lp == NULL)
- lp = add_lwp (ptid);
-
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
"LLAL: PTRACE_ATTACH %s, 0, 0 (OK)\n",
{
/* Try again with __WCLONE to check cloned processes. */
pid = my_waitpid (GET_LWP (ptid), &status, __WCLONE);
- lp->cloned = 1;
+ cloned = 1;
}
gdb_assert (pid == GET_LWP (ptid)
&& WIFSTOPPED (status) && WSTOPSIG (status));
+ if (lp == NULL)
+ lp = add_lwp (ptid);
+ lp->cloned = cloned;
+
target_post_attach (pid);
lp->stopped = 1;
struct lwp_info *lp;
pid_t pid;
int status;
+ int cloned = 0;
/* FIXME: We should probably accept a list of process id's, and
attach all of them. */
linux_ops->to_attach (args, from_tty);
- /* Add the initial process as the first LWP to the list. */
- inferior_ptid = BUILD_LWP (GET_PID (inferior_ptid), GET_PID (inferior_ptid));
- lp = add_lwp (inferior_ptid);
-
/* Make sure the initial process is stopped. The user-level threads
layer might want to poke around in the inferior, and that won't
work if things haven't stabilized yet. */
/* Try again with __WCLONE to check cloned processes. */
pid = my_waitpid (GET_PID (inferior_ptid), &status, __WCLONE);
- lp->cloned = 1;
+ cloned = 1;
}
gdb_assert (pid == GET_PID (inferior_ptid)
&& WIFSTOPPED (status) && WSTOPSIG (status) == SIGSTOP);
+ /* Add the initial process as the first LWP to the list. */
+ inferior_ptid = BUILD_LWP (GET_PID (inferior_ptid), GET_PID (inferior_ptid));
+ lp = add_lwp (inferior_ptid);
+ lp->cloned = cloned;
+
lp->stopped = 1;
/* Fake the SIGSTOP that core GDB expects. */
{
if (lp->stopped && lp->status == 0)
{
- struct thread_info *tp;
-
linux_ops->to_resume (pid_to_ptid (GET_LWP (lp->ptid)),
0, TARGET_SIGNAL_0);
if (debug_linux_nat)
target_pid_to_str (lp->ptid));
lp->stopped = 0;
lp->step = 0;
+ memset (&lp->siginfo, 0, sizeof (lp->siginfo));
}
return 0;
ptid = inferior_ptid;
lp = find_lwp_pid (ptid);
- if (lp)
- {
- ptid = pid_to_ptid (GET_LWP (lp->ptid));
+ gdb_assert (lp != NULL);
- /* Remember if we're stepping. */
- lp->step = step;
+ ptid = pid_to_ptid (GET_LWP (lp->ptid));
- /* Mark this LWP as resumed. */
- lp->resumed = 1;
+ /* Remember if we're stepping. */
+ lp->step = step;
- /* If we have a pending wait status for this thread, there is no
- point in resuming the process. But first make sure that
- linux_nat_wait won't preemptively handle the event - we
- should never take this short-circuit if we are going to
- leave LP running, since we have skipped resuming all the
- other threads. This bit of code needs to be synchronized
- with linux_nat_wait. */
+ /* Mark this LWP as resumed. */
+ lp->resumed = 1;
- if (lp->status && WIFSTOPPED (lp->status))
- {
- int saved_signo = target_signal_from_host (WSTOPSIG (lp->status));
+ /* If we have a pending wait status for this thread, there is no
+ point in resuming the process. But first make sure that
+ linux_nat_wait won't preemptively handle the event - we
+ should never take this short-circuit if we are going to
+ leave LP running, since we have skipped resuming all the
+ other threads. This bit of code needs to be synchronized
+ with linux_nat_wait. */
- if (signal_stop_state (saved_signo) == 0
- && signal_print_state (saved_signo) == 0
- && signal_pass_state (saved_signo) == 1)
- {
- if (debug_linux_nat)
- fprintf_unfiltered (gdb_stdlog,
- "LLR: Not short circuiting for ignored "
- "status 0x%x\n", lp->status);
-
- /* FIXME: What should we do if we are supposed to continue
- this thread with a signal? */
- gdb_assert (signo == TARGET_SIGNAL_0);
- signo = saved_signo;
- lp->status = 0;
- }
- }
+ if (lp->status && WIFSTOPPED (lp->status))
+ {
+ int saved_signo = target_signal_from_host (WSTOPSIG (lp->status));
- if (lp->status)
+ if (signal_stop_state (saved_signo) == 0
+ && signal_print_state (saved_signo) == 0
+ && signal_pass_state (saved_signo) == 1)
{
+ if (debug_linux_nat)
+ fprintf_unfiltered (gdb_stdlog,
+ "LLR: Not short circuiting for ignored "
+ "status 0x%x\n", lp->status);
+
/* FIXME: What should we do if we are supposed to continue
this thread with a signal? */
gdb_assert (signo == TARGET_SIGNAL_0);
+ signo = saved_signo;
+ lp->status = 0;
+ }
+ }
- if (debug_linux_nat)
- fprintf_unfiltered (gdb_stdlog,
- "LLR: Short circuiting for status 0x%x\n",
- lp->status);
+ if (lp->status)
+ {
+ /* FIXME: What should we do if we are supposed to continue
+ this thread with a signal? */
+ gdb_assert (signo == TARGET_SIGNAL_0);
- return;
- }
+ if (debug_linux_nat)
+ fprintf_unfiltered (gdb_stdlog,
+ "LLR: Short circuiting for status 0x%x\n",
+ lp->status);
- /* Mark LWP as not stopped to prevent it from being continued by
- resume_callback. */
- lp->stopped = 0;
+ return;
}
+ /* Mark LWP as not stopped to prevent it from being continued by
+ resume_callback. */
+ lp->stopped = 0;
+
if (resume_all)
iterate_over_lwps (resume_callback, NULL);
linux_ops->to_resume (ptid, step, signo);
+ memset (&lp->siginfo, 0, sizeof (lp->siginfo));
+
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
"LLR: %s %s, %s (resume event thread)\n",
return status;
}
+/* Save the most recent siginfo for LP. This is currently only called
+ for SIGTRAP; some ports use the si_addr field for
+ target_stopped_data_address. In the future, it may also be used to
+ restore the siginfo of requeued signals. */
+
+static void
+save_siginfo (struct lwp_info *lp)
+{
+ errno = 0;
+ ptrace (PTRACE_GETSIGINFO, GET_LWP (lp->ptid),
+ (PTRACE_TYPE_ARG3) 0, &lp->siginfo);
+
+ if (errno != 0)
+ memset (&lp->siginfo, 0, sizeof (lp->siginfo));
+}
+
/* Send a SIGSTOP to LP. */
static int
user will delete or disable the breakpoint, but the
thread will have already tripped on it. */
+ /* Save the trap's siginfo in case we need it later. */
+ save_siginfo (lp);
+
/* Now resume this LWP and get the SIGSTOP event. */
errno = 0;
ptrace (PTRACE_CONT, GET_LWP (lp->ptid), 0, 0);
target_pid_to_str (lp->ptid));
}
+ /* Save the trap's siginfo in case we need it later. */
+ if (WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP)
+ save_siginfo (lp);
+
/* Handle GNU/Linux's extended waitstatus for trace events. */
if (WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP && status >> 16 != 0)
{
thread_db_init (t);
}
+/* Register a method to call whenever a new thread is attached. */
+void
+linux_nat_set_new_thread (struct target_ops *t, void (*new_thread) (ptid_t))
+{
+ /* Save the pointer. We only support a single registered instance
+ of the GNU/Linux native target, so we do not need to map this to
+ T. */
+ linux_nat_new_thread = new_thread;
+}
+
+/* Return the saved siginfo associated with PTID. */
+struct siginfo *
+linux_nat_get_siginfo (ptid_t ptid)
+{
+ struct lwp_info *lp = find_lwp_pid (ptid);
+
+ gdb_assert (lp != NULL);
+
+ return &lp->siginfo;
+}
+
void
_initialize_linux_nat (void)
{
#include "target.h"
-/* Structure describing an LWP. */
+#include <signal.h>
+
+/* Structure describing an LWP. This is public only for the purposes
+ of ALL_LWPS; target-specific code should generally not access it
+ directly. */
struct lwp_info
{
/* Non-zero if we were stepping this LWP. */
int step;
+ /* Non-zero si_signo if this LWP stopped with a trap. si_addr may
+ be the address of a hardware watchpoint. */
+ struct siginfo siginfo;
+
/* If WAITSTATUS->KIND != TARGET_WAITKIND_SPURIOUS, the waitstatus
for this LWP's last event. This may correspond to STATUS above,
or to a local variable in lin_lwp_wait. */
struct lwp_info *next;
};
+/* The global list of LWPs, for ALL_LWPS. Unlike the threads list,
+ there is always at least one LWP on the list while the GNU/Linux
+ native target is active. */
+extern struct lwp_info *lwp_list;
+
+/* Iterate over the PTID each active thread (light-weight process). There
+ must be at least one. */
+#define ALL_LWPS(LP, PTID) \
+ for ((LP) = lwp_list, (PTID) = (LP)->ptid; \
+ (LP) != NULL; \
+ (LP) = (LP)->next, (PTID) = (LP) ? (LP)->ptid : (PTID))
+
/* Attempt to initialize libthread_db. */
void check_for_thread_db (void);
instead of calling add_target directly. */
void linux_nat_add_target (struct target_ops *);
+/* Register a method to call whenever a new thread is attached. */
+void linux_nat_set_new_thread (struct target_ops *, void (*) (ptid_t));
+
/* Update linux-nat internal state when changing from one fork
to another. */
void linux_nat_switch_fork (ptid_t new_ptid);
+
+/* Return the saved siginfo associated with PTID. */
+struct siginfo *linux_nat_get_siginfo (ptid_t ptid);
error. */
int have_ptrace_getvrregs = 1;
-static CORE_ADDR last_stopped_data_address = 0;
-
/* Non-zero if our kernel may support the PTRACE_GETEVRREGS and
PTRACE_SETEVRREGS requests, for reading and writing the SPE
registers. Zero if we've tried one of them and gotten an
return 1;
}
+/* The cached DABR value, to install in new threads. */
+static long saved_dabr_value;
+
/* Set a watchpoint of type TYPE at address ADDR. */
static int
ppc_linux_insert_watchpoint (CORE_ADDR addr, int len, int rw)
{
- int tid;
+ struct lwp_info *lp;
+ ptid_t ptid;
long dabr_value;
- ptid_t ptid = inferior_ptid;
dabr_value = addr & ~7;
switch (rw)
break;
}
- tid = TIDGET (ptid);
- if (tid == 0)
- tid = PIDGET (ptid);
-
- return ptrace (PTRACE_SET_DEBUGREG, tid, 0, dabr_value);
+ ALL_LWPS (lp, ptid)
+ if (ptrace (PTRACE_SET_DEBUGREG, TIDGET (ptid), 0, saved_dabr_value) < 0)
+ return -1;
+ saved_dabr_value = dabr_value;
+ return 0;
}
static int
ppc_linux_remove_watchpoint (CORE_ADDR addr, int len, int rw)
{
- int tid;
- ptid_t ptid = inferior_ptid;
-
- tid = TIDGET (ptid);
- if (tid == 0)
- tid = PIDGET (ptid);
-
- return ptrace (PTRACE_SET_DEBUGREG, tid, 0, 0);
+ struct lwp_info *lp;
+ ptid_t ptid;
+ long dabr_value = 0;
+
+ saved_dabr_value = 0;
+ ALL_LWPS (lp, ptid)
+ if (ptrace (PTRACE_SET_DEBUGREG, TIDGET (ptid), 0, saved_dabr_value) < 0)
+ return -1;
+ return 0;
}
-static int
-ppc_linux_stopped_data_address (struct target_ops *target, CORE_ADDR *addr_p)
+static void
+ppc_linux_new_thread (ptid_t ptid)
{
- if (last_stopped_data_address)
- {
- *addr_p = last_stopped_data_address;
- last_stopped_data_address = 0;
- return 1;
- }
- return 0;
+ ptrace (PTRACE_SET_DEBUGREG, TIDGET (ptid), 0, saved_dabr_value);
}
static int
-ppc_linux_stopped_by_watchpoint (void)
+ppc_linux_stopped_data_address (struct target_ops *target, CORE_ADDR *addr_p)
{
- int tid;
- struct siginfo siginfo;
- ptid_t ptid = inferior_ptid;
- CORE_ADDR *addr_p;
+ struct siginfo *siginfo_p;
- tid = TIDGET(ptid);
- if (tid == 0)
- tid = PIDGET (ptid);
+ siginfo_p = linux_nat_get_siginfo (inferior_ptid);
- errno = 0;
- ptrace (PTRACE_GETSIGINFO, tid, (PTRACE_TYPE_ARG3) 0, &siginfo);
-
- if (errno != 0 || siginfo.si_signo != SIGTRAP ||
- (siginfo.si_code & 0xffff) != 0x0004)
+ if (siginfo_p->si_signo != SIGTRAP
+ || (siginfo_p->si_code & 0xffff) != 0x0004 /* TRAP_HWBKPT */)
return 0;
- last_stopped_data_address = (uintptr_t) siginfo.si_addr;
+ *addr_p = (CORE_ADDR) siginfo_p->si_addr;
return 1;
}
+static int
+ppc_linux_stopped_by_watchpoint (void)
+{
+ CORE_ADDR addr;
+ return ppc_linux_stopped_data_address (¤t_target, &addr);
+}
+
static void
ppc_linux_store_inferior_registers (struct regcache *regcache, int regno)
{
/* Register the target. */
linux_nat_add_target (t);
+ linux_nat_set_new_thread (t, ppc_linux_new_thread);
}
{
per_lowcore_bits per_lowcore;
ptrace_area parea;
+ int result;
/* Speed up common case. */
if (!watch_base)
if (ptrace (PTRACE_PEEKUSR_AREA, s390_inferior_tid (), &parea) < 0)
perror_with_name (_("Couldn't retrieve watchpoint status"));
- return per_lowcore.perc_storage_alteration == 1
- && per_lowcore.perc_store_real_address == 0;
+ result = (per_lowcore.perc_storage_alteration == 1
+ && per_lowcore.perc_store_real_address == 0);
+
+ if (result)
+ {
+ /* Do not report this watchpoint again. */
+ memset (&per_lowcore, 0, sizeof (per_lowcore));
+ if (ptrace (PTRACE_POKEUSR_AREA, s390_inferior_tid (), &parea) < 0)
+ perror_with_name (_("Couldn't clear watchpoint status"));
+ }
+
+ return result;
}
static void
-s390_fix_watch_points (void)
+s390_fix_watch_points (ptid_t ptid)
{
- int tid = s390_inferior_tid ();
+ int tid;
per_struct per_info;
ptrace_area parea;
CORE_ADDR watch_lo_addr = (CORE_ADDR)-1, watch_hi_addr = 0;
struct watch_area *area;
+ tid = TIDGET (ptid);
+ if (tid == 0)
+ tid = PIDGET (ptid);
+
for (area = watch_base; area; area = area->next)
{
watch_lo_addr = min (watch_lo_addr, area->lo_addr);
static int
s390_insert_watchpoint (CORE_ADDR addr, int len, int type)
{
+ struct lwp_info *lp;
+ ptid_t ptid;
struct watch_area *area = xmalloc (sizeof (struct watch_area));
+
if (!area)
return -1;
area->next = watch_base;
watch_base = area;
- s390_fix_watch_points ();
+ ALL_LWPS (lp, ptid)
+ s390_fix_watch_points (ptid);
return 0;
}
static int
s390_remove_watchpoint (CORE_ADDR addr, int len, int type)
{
+ struct lwp_info *lp;
+ ptid_t ptid;
struct watch_area *area, **parea;
for (parea = &watch_base; *parea; parea = &(*parea)->next)
*parea = area->next;
xfree (area);
- s390_fix_watch_points ();
+ ALL_LWPS (lp, ptid)
+ s390_fix_watch_points (ptid);
return 0;
}
/* Register the target. */
linux_nat_add_target (t);
+ linux_nat_set_new_thread (t, s390_fix_watch_points);
}