diff options
author | Jan Kratochvil <jan.kratochvil@redhat.com> | 2012-01-24 13:46:55 +0000 |
---|---|---|
committer | Jan Kratochvil <jan.kratochvil@redhat.com> | 2012-01-24 13:46:55 +0000 |
commit | 4403d8e9b35649c5b24f65c0ec0decc3839e1164 (patch) | |
tree | 05ba9cdf77784672a115e5576715b31fc7df7873 /gdb/linux-nat.c | |
parent | 2992c9a7112fd3b97cd693b743639e01baea3003 (diff) | |
download | gdb-4403d8e9b35649c5b24f65c0ec0decc3839e1164.zip gdb-4403d8e9b35649c5b24f65c0ec0decc3839e1164.tar.gz gdb-4403d8e9b35649c5b24f65c0ec0decc3839e1164.tar.bz2 |
gdb/
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.
Diffstat (limited to 'gdb/linux-nat.c')
-rw-r--r-- | gdb/linux-nat.c | 104 |
1 files changed, 89 insertions, 15 deletions
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c index 50d4efd..6aab087 100644 --- a/gdb/linux-nat.c +++ b/gdb/linux-nat.c @@ -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 |