Fix watchpoints across inferior fork.
	* amd64-linux-nat.c (update_debug_registers_callback): Update the
	comment for linux_nat_iterate_watchpoint_lwps.
	(amd64_linux_dr_set_control, amd64_linux_dr_set_addr): Use
	linux_nat_iterate_watchpoint_lwps.
	(amd64_linux_prepare_to_resume): New comment on Linux kernel.
	* i386-linux-nat.c (update_debug_registers_callback): Update the
	comment for linux_nat_iterate_watchpoint_lwps.
	(i386_linux_dr_set_control, i386_linux_dr_set_addr): Use
	linux_nat_iterate_watchpoint_lwps.
	(i386_linux_prepare_to_resume): New comment on Linux kernel.
	* i386-nat.c: Include inferior.h.
	(dr_mirror): Remove.
	(i386_inferior_data, struct i386_inferior_data)
	(i386_inferior_data_get): New.
	(i386_debug_reg_state): Use i386_inferior_data_get.
	(i386_cleanup_dregs, i386_update_inferior_debug_regs)
	(i386_insert_watchpoint, i386_remove_watchpoint)
	(i386_stopped_data_address, i386_insert_hw_breakpoint)
	(i386_remove_hw_breakpoint): New variable state, use
	i386_debug_reg_state instead of DR_MIRROR.
	* linux-nat.c (delete_lwp): New declaration.
	(num_lwps): Move here from downwards.
	(delete_lwp_cleanup): New.
	(linux_child_follow_fork): Create new child_lp, call
	linux_nat_new_thread and linux_nat_prepare_to_resume before calling
	PTRACE_DETACH.
	(num_lwps): Move upwards.
	(linux_nat_iterate_watchpoint_lwps): New.
	* linux-nat.h (linux_nat_iterate_watchpoint_lwps_ftype): New.
	(linux_nat_iterate_watchpoint_lwps_ftype): New declaration.

gdb/testsuite/
	Fix watchpoints across inferior fork.
	* gdb.threads/watchpoint-fork-child.c: New file.
	* gdb.threads/watchpoint-fork-mt.c: New file.
	* gdb.threads/watchpoint-fork-parent.c: New file.
	* gdb.threads/watchpoint-fork-st.c: New file.
	* gdb.threads/watchpoint-fork.exp: New file.
	* gdb.threads/watchpoint-fork.h: New file.
This commit is contained in:
Jan Kratochvil
2012-01-24 13:46:55 +00:00
parent 2992c9a711
commit 4403d8e9b3
13 changed files with 896 additions and 68 deletions

View File

@@ -288,6 +288,7 @@ static void restore_child_signals_mask (sigset_t *prev_mask);
struct lwp_info;
static struct lwp_info *add_lwp (ptid_t ptid);
static void purge_lwp_list (int pid);
static void delete_lwp (ptid_t ptid);
static struct lwp_info *find_lwp_pid (ptid_t ptid);
@@ -584,6 +585,31 @@ linux_child_post_startup_inferior (ptid_t ptid)
linux_enable_tracesysgood (ptid);
}
/* Return the number of known LWPs in the tgid given by PID. */
static int
num_lwps (int pid)
{
int count = 0;
struct lwp_info *lp;
for (lp = lwp_list; lp; lp = lp->next)
if (ptid_get_pid (lp->ptid) == pid)
count++;
return count;
}
/* Call delete_lwp with prototype compatible for make_cleanup. */
static void
delete_lwp_cleanup (void *lp_voidp)
{
struct lwp_info *lp = lp_voidp;
delete_lwp (lp->ptid);
}
static int
linux_child_follow_fork (struct target_ops *ops, int follow_child)
{
@@ -630,6 +656,8 @@ holding the child stopped. Try \"set detach-on-fork\" or \
/* Detach new forked process? */
if (detach_fork)
{
struct cleanup *old_chain;
/* Before detaching from the child, remove all breakpoints
from it. If we forked, then this has already been taken
care of by infrun.c. If we vforked however, any
@@ -652,7 +680,28 @@ holding the child stopped. Try \"set detach-on-fork\" or \
child_pid);
}
old_chain = save_inferior_ptid ();
inferior_ptid = ptid_build (child_pid, child_pid, 0);
child_lp = add_lwp (inferior_ptid);
child_lp->stopped = 1;
child_lp->last_resume_kind = resume_stop;
make_cleanup (delete_lwp_cleanup, child_lp);
/* CHILD_LP has new PID, therefore linux_nat_new_thread is not called for it.
See i386_inferior_data_get for the Linux kernel specifics.
Ensure linux_nat_prepare_to_resume will reset the hardware debug
registers. It is done by the linux_nat_new_thread call, which is
being skipped in add_lwp above for the first lwp of a pid. */
gdb_assert (num_lwps (GET_PID (child_lp->ptid)) == 1);
if (linux_nat_new_thread != NULL)
linux_nat_new_thread (child_lp);
if (linux_nat_prepare_to_resume != NULL)
linux_nat_prepare_to_resume (child_lp);
ptrace (PTRACE_DETACH, child_pid, 0, 0);
do_cleanups (old_chain);
}
else
{
@@ -1111,21 +1160,6 @@ purge_lwp_list (int pid)
}
}
/* Return the number of known LWPs in the tgid given by PID. */
static int
num_lwps (int pid)
{
int count = 0;
struct lwp_info *lp;
for (lp = lwp_list; lp; lp = lp->next)
if (ptid_get_pid (lp->ptid) == pid)
count++;
return count;
}
/* Add the LWP specified by PID to the list. Return a pointer to the
structure describing the new LWP. The LWP should already be stopped
(with an exception for the very first LWP). */
@@ -1235,6 +1269,46 @@ iterate_over_lwps (ptid_t filter,
return NULL;
}
/* Iterate like iterate_over_lwps does except when forking-off a child call
CALLBACK with CALLBACK_DATA specifically only for that new child PID. */
void
linux_nat_iterate_watchpoint_lwps
(linux_nat_iterate_watchpoint_lwps_ftype callback, void *callback_data)
{
int inferior_pid = ptid_get_pid (inferior_ptid);
struct inferior *inf = current_inferior ();
if (inf->pid == inferior_pid)
{
/* Iterate all the threads of the current inferior. Without specifying
INFERIOR_PID it would iterate all threads of all inferiors, which is
inappropriate for watchpoints. */
iterate_over_lwps (pid_to_ptid (inferior_pid), callback, callback_data);
}
else
{
/* Detaching a new child PID temporarily present in INFERIOR_PID. */
struct lwp_info *child_lp;
struct cleanup *old_chain;
pid_t child_pid = GET_PID (inferior_ptid);
ptid_t child_ptid = ptid_build (child_pid, child_pid, 0);
gdb_assert (!is_lwp (inferior_ptid));
gdb_assert (find_lwp_pid (child_ptid) == NULL);
child_lp = add_lwp (child_ptid);
child_lp->stopped = 1;
child_lp->last_resume_kind = resume_stop;
old_chain = make_cleanup (delete_lwp_cleanup, child_lp);
callback (child_lp, callback_data);
do_cleanups (old_chain);
}
}
/* Update our internal state when changing from one checkpoint to
another indicated by NEW_PTID. We can only switch single-threaded
applications, so we only create one new LWP, and the previous list