[rfc] Multi-threaded watchpoints, GNU/Linux native bits

Daniel Jacobowitz
2007-09-16T18:57:48Z

Here are the GNU/Linux-specific changes for multi-threaded
watchpoints. They are fairly mechanical and fall into two categories:
when setting watchpoints, set them in all threads; and when a new
thread is attached, make sure to set the same watchpoints there that
are set in existing threads. The latter half is probably not
necessary in practice, but insulates GDB from any kernel changes
in what state is copied across clone().

This patch is pretty mechanical really.

The observers from earlier versions are completely gone. Instead the
siginfo data is always saved (it will be useful for other things, so
we might as well) and the new thread events are handled via target
vector inheritance.

Like the last patch, tested on x86_64-linux; earlier versions tested
on all the affected targets. After this goes in, I think a NEWS entry
will be called for. And watchthreads.exp now passes!

--
Daniel Jacobowitz
CodeSourcery

2007-09-16 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.

--- amd64-linux-nat.c 23 Aug 2007 18:08:26 -0000 1.17
+++ amd64-linux-nat.c 16 Sep 2007 18:05:27 -0000
@@ -233,25 +233,27 @@ amd64_linux_store_inferior_registers (st
}
}
\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
@@ -264,17 +266,17 @@ amd64_linux_dr_get (int regnum)
}

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"));
}
@@ -282,29 +284,48 @@ amd64_linux_dr_set (int regnum, unsigned
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

@@ -408,4 +429,5 @@ _initialize_amd64_linux_nat (void)

/* Register the target. */
linux_nat_add_target (t);
+ linux_nat_set_new_thread (t, amd64_linux_new_thread);
}
--- i386-linux-nat.c 23 Aug 2007 18:08:34 -0000 1.82
+++ i386-linux-nat.c 16 Sep 2007 18:05:28 -0000
@@ -579,16 +579,17 @@ i386_linux_store_inferior_registers (str

/* 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
@@ -609,14 +610,13 @@ i386_linux_dr_get (int regnum)
}

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,
@@ -628,29 +628,48 @@ i386_linux_dr_set (int regnum, unsigned
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

@@ -729,12 +748,6 @@ i386_linux_resume (ptid_t ptid, int step

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));
@@ -818,4 +831,5 @@ _initialize_i386_linux_nat (void)

/* Register the target. */
linux_nat_add_target (t);
+ linux_nat_set_new_thread (t, i386_linux_new_thread);
}
--- ia64-linux-nat.c 23 Aug 2007 18:08:35 -0000 1.40
+++ ia64-linux-nat.c 16 Sep 2007 18:05:28 -0000
@@ -479,8 +479,9 @@ fill_fpregset (const struct regcache *re
#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);
@@ -492,20 +493,7 @@ enable_watchpoints_in_psr (struct regcac
}
}

-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)
@@ -520,15 +508,6 @@ store_debug_register (ptid_t ptid, int i
}

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)
{
if (dbr_addr)
@@ -553,7 +532,8 @@ is_power_of_2 (int val)
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;
@@ -563,7 +543,7 @@ ia64_linux_insert_watchpoint (CORE_ADDR

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 */
@@ -592,8 +572,13 @@ ia64_linux_insert_watchpoint (CORE_ADDR
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;
}
@@ -601,7 +586,6 @@ ia64_linux_insert_watchpoint (CORE_ADDR
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;
@@ -611,36 +595,55 @@ ia64_linux_remove_watchpoint (CORE_ADDR

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);
@@ -648,7 +651,7 @@ ia64_linux_stopped_data_address (struct
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;
}

@@ -834,4 +837,5 @@ _initialize_ia64_linux_nat (void)

/* Register the target. */
linux_nat_add_target (t);
+ linux_nat_set_new_thread (t, ia64_linux_new_thread);
}
--- linux-nat.c 2 Sep 2007 14:04:31 -0000 1.67
+++ linux-nat.c 16 Sep 2007 18:05:28 -0000
@@ -89,6 +89,9 @@
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 *,
@@ -503,12 +506,13 @@ linux_child_follow_fork (struct target_o
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 ();
@@ -573,7 +577,7 @@ linux_child_insert_exec_catchpoint (int
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;
@@ -657,7 +661,8 @@ init_lwp_list (void)
}

/* 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)
@@ -678,6 +683,9 @@ 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;
}

@@ -884,6 +892,7 @@ lin_lwp_attach_lwp (ptid_t ptid, int ver
{
pid_t pid;
int status;
+ int cloned = 0;

if (ptrace (PTRACE_ATTACH, GET_LWP (ptid), 0, 0) < 0)
{
@@ -897,9 +906,6 @@ lin_lwp_attach_lwp (ptid_t ptid, int ver
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",
@@ -910,12 +916,16 @@ lin_lwp_attach_lwp (ptid_t ptid, int ver
{
/* 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;
@@ -953,15 +963,12 @@ linux_nat_attach (char *args, int from_t
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. */
@@ -972,12 +979,17 @@ linux_nat_attach (char *args, int from_t

/* 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. */
@@ -1077,8 +1089,6 @@ resume_callback (struct lwp_info *lp, vo
{
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)
@@ -1087,6 +1097,7 @@ resume_callback (struct lwp_info *lp, vo
target_pid_to_str (lp->ptid));
lp->stopped = 0;
lp->step = 0;
+ memset (&lp->siginfo, 0, sizeof (lp->siginfo));
}

return 0;
@@ -1136,68 +1147,69 @@ linux_nat_resume (ptid_t ptid, int step,
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;
-
- /* Mark this LWP as resumed. */
- lp->resumed = 1;
+ ptid = pid_to_ptid (GET_LWP (lp->ptid));

- /* 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 (lp->status && WIFSTOPPED (lp->status))
- {
- int saved_signo = target_signal_from_host (WSTOPSIG (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);
+ /* Remember if we're stepping. */
+ lp->step = step;

- /* 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;
- }
- }
+ /* Mark this LWP as resumed. */
+ lp->resumed = 1;

- if (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 (lp->status && WIFSTOPPED (lp->status))
+ {
+ int saved_signo = target_signal_from_host (WSTOPSIG (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",
@@ -1416,6 +1428,22 @@ wait_lwp (struct lwp_info *lp)
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
@@ -1501,6 +1529,9 @@ stop_wait_callback (struct lwp_info *lp,
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);
@@ -2058,6 +2089,10 @@ retry:
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)
{
@@ -3250,6 +3285,27 @@ linux_nat_add_target (struct target_ops
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)
{
--- linux-nat.h 23 Aug 2007 18:08:35 -0000 1.19
+++ linux-nat.h 16 Sep 2007 18:05:28 -0000
@@ -20,7 +20,11 @@

#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
{
@@ -54,6 +58,10 @@ 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. */
@@ -63,6 +71,18 @@ struct lwp_info
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);

@@ -95,6 +115,12 @@ linux_trad_target (CORE_ADDR (*register_
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);
--- ppc-linux-nat.c 30 Aug 2007 13:13:59 -0000 1.70
+++ ppc-linux-nat.c 16 Sep 2007 18:05:28 -0000
@@ -142,8 +142,6 @@ struct gdb_evrregset_t
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
@@ -805,13 +803,16 @@ ppc_linux_region_ok_for_hw_watchpoint (C
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)
@@ -830,61 +831,56 @@ ppc_linux_insert_watchpoint (CORE_ADDR a
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;
+ struct siginfo *siginfo_p;
CORE_ADDR *addr_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 (&current_target, &addr);
+}
+
static void
ppc_linux_store_inferior_registers (struct regcache *regcache, int regno)
{
@@ -969,4 +965,5 @@ _initialize_ppc_linux_nat (void)

/* Register the target. */
linux_nat_add_target (t);
+ linux_nat_set_new_thread (t, ppc_linux_new_thread);
}
--- s390-nat.c 23 Aug 2007 18:08:37 -0000 1.25
+++ s390-nat.c 16 Sep 2007 18:05:29 -0000
@@ -252,6 +252,7 @@ s390_stopped_by_watchpoint (void)
{
per_lowcore_bits per_lowcore;
ptrace_area parea;
+ int result;

/* Speed up common case. */
if (!watch_base)
@@ -263,14 +264,24 @@ s390_stopped_by_watchpoint (void)
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;
@@ -278,6 +289,10 @@ s390_fix_watch_points (void)
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);
@@ -310,7 +325,10 @@ s390_fix_watch_points (void)
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;

@@ -320,13 +338,16 @@ s390_insert_watchpoint (CORE_ADDR addr,
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)
@@ -345,7 +366,8 @@ s390_remove_watchpoint (CORE_ADDR addr,
*parea = area->next;
xfree (area);

- s390_fix_watch_points ();
+ ALL_LWPS (lp, ptid)
+ s390_fix_watch_points (ptid);
return 0;
}

@@ -386,4 +408,5 @@ _initialize_s390_nat (void)

/* Register the target. */
linux_nat_add_target (t);
+ linux_nat_set_new_thread (t, s390_fix_watch_points);
}