[native x86 GNU/Linux] Access debug register mirror from the corresponding process.

While reviewing the native AArch64 patch, I noticed a problem:

On 02/06/2013 08:46 PM, Pedro Alves wrote:
>
>> > +static void
>> > +aarch64_linux_prepare_to_resume (struct lwp_info *lwp)
>> > +{
>> > +  struct arch_lwp_info *info = lwp->arch_private;
>> > +
>> > +  /* NULL means this is the main thread still going through the shell,
>> > +     or, no watchpoint has been set yet.  In that case, there's
>> > +     nothing to do.  */
>> > +  if (info == NULL)
>> > +    return;
>> > +
>> > +  if (DR_HAS_CHANGED (info->dr_changed_bp)
>> > +      || DR_HAS_CHANGED (info->dr_changed_wp))
>> > +    {
>> > +      int tid = GET_LWP (lwp->ptid);
>> > +      struct aarch64_debug_reg_state *state = aarch64_get_debug_reg_state ();
> Hmm.  This is always fetching the debug_reg_state of
> the current inferior, but may not be the inferior of lwp.
> I see the same bug on x86.  Sorry about that.  I'll fix it.

A natural fix would be to make xxx_get_debug_reg_state take an
inferior argument, but that doesn't work because of the case where we
detach breakpoints/watchpoints from the child fork, at a time there's
no inferior for the child fork at all.  We do a nasty hack in
i386_inferior_data_get, but that relies on all callers pointing the
current inferior to the correct inferior, which isn't actually being
done by all callers, and I don't think we want to enforce that -- deep
in the bowls of linux-nat.c, there are many cases we resume lwps
behind the scenes, and it's be better to not have that code rely on
global state (as it doesn't today).

The fix is to decouple the watchpoints code from inferiors, making it
track target processes instead.  This way, we can freely keep track of
the watchpoint mirrors for these processes behind the core's back.
Checkpoints also play dirty tricks with swapping the process behind
the inferior, so they get special treatment too in the patch (which
just amounts to calling a new hook).  Instead of the old hack in
i386_inferior_data_get, where we returned a copy of the current
inferior's debug registers mirror, as soon as we detect a fork in the
target, we copy the debug register mirror from the parent to the child
process.

I don't have an old kernel handy to test, but I stepped through gdb doing
the watchpoint removal in the fork child in the watchpoint-fork test
seeing that the debug registers end up cleared in the child.

I didn't find the need for linux_nat_iterate_watchpoint_lwps.  If
we use plain iterate_over_lwps instead, what happens is that
when removing watchpoints, that iterate_over_lwps doesn't actually
iterate over anything, since the fork child is not added to the
lwp list until later, at detach time, in linux_child_follow_fork.
And if we don't iterate over that lwp, we don't mark its debug
registers as needing update.  But linux_child_follow_fork takes
care of doing that explicitly:

	  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);

so unless I'm missing something (quite possible) it ends up all
the same.  But, the !detach-on-fork, and the "follow-fork child" paths
should also call linux_nat_new_thread, and they don't presently.  It
seems to me in those cases we're not clearing debug regs correctly
when that's needed.  Instead of copying that bit that works around
add_lwp bypassing the linux_nat_new_thread call, I thought it'd
be better to add an add_initial_lwp call to be used in the case we
really need to bypass linux_nat_new_thread, and make
add_lwp always call linux_nat_new_thread.

i386_cleanup_dregs is rewritten to forget about the current process
debug mirrors, which takes cares of other i386 ports.  Only a couple
of extra tweaks here and there were needed, as some targets wheren't
actually calling i386_cleanup_dregs.

Tested on Fedora 17 x86_64 -m64/-m32.

GDBserver already fetches the i386_debug_reg_state from the right
process, and, it doesn't handle forks at all, so no fix is needed over
there.

gdb/
2013-02-13  Pedro Alves  <palves@redhat.com>

	* amd64-linux-nat.c (update_debug_registers_callback):
	Update comment.
	(amd64_linux_dr_set_control, amd64_linux_dr_set_addr): Use
	iterate_over_lwps.
	(amd64_linux_prepare_to_resume): Pass the lwp's pid to
	i386_debug_reg_state.
	(amd64_linux_new_fork): New function.
	(_initialize_amd64_linux_nat): Install amd64_linux_new_fork as
	linux_nat_new_fork hook, and i386_forget_process as
	linux_nat_forget_process hook.
	* i386-linux-nat.c (update_debug_registers_callback):
	Update comment.
	(amd64_linux_dr_set_control, amd64_linux_dr_set_addr): Use
	iterate_over_lwps.
	(i386_linux_prepare_to_resume): Pass the lwp's pid to
	i386_debug_reg_state.
	(i386_linux_new_fork): New function.
	(_initialize_i386_linux_nat): Install i386_linux_new_fork as
	linux_nat_new_fork hook, and i386_forget_process as
	linux_nat_forget_process hook.
	* i386-nat.c (i386_init_dregs): Delete.
	(i386_inferior_data, struct i386_inferior_data):
	Delete.
	(struct i386_process_info): New.
	(i386_process_list): New global.
	(i386_find_process_pid, i386_add_process, i386_process_info_get):
	New functions.
	(i386_inferior_data_get): Delete.
	(i386_process_info_get): New function.
	(i386_debug_reg_state): New parameter 'pid'.  Reimplement.
	(i386_forget_process): New function.
	(i386_cleanup_dregs): Rewrite.
	(i386_update_inferior_debug_regs, i386_insert_watchpoint)
	(i386_remove_watchpoint, i386_region_ok_for_watchpoint)
	(i386_stopped_data_address, i386_insert_hw_breakpoint)
	(i386_remove_hw_breakpoint): Adjust to pass the current process id
	to i386_debug_reg_state.
	(i386_use_watchpoints): Don't register inferior data.
	* i386-nat.h (i386_debug_reg_state): Add new 'pid' parameter, and
	adjust comment.
	(i386_forget_process): Declare.
	* linux-fork.c (delete_fork): Call linux_nat_forget_process.
	* linux-nat.c (linux_nat_new_fork, linux_nat_forget_process_hook):
	New static globals.
	(linux_child_follow_fork): Don't call linux_nat_new_thread here.
	(add_initial_lwp): New, factored out from ...
	(add_lwp): ... this.  Don't check the number of lwps before
	calling linux_nat_new_thread.
	(linux_nat_iterate_watchpoint_lwps): Delete.
	(linux_nat_attach): Use add_initial_lwp instead of add_lwp.
	(linux_handle_extended_wait): Call the linux_nat_new_fork hook on
	forks and vforks.
	(linux_nat_wait_1): Use add_initial_lwp instead of add_lwp for the
	initial lwp.
	(linux_nat_kill, linux_nat_mourn_inferior): Call
	linux_nat_forget_process.
	(linux_nat_set_new_fork, linux_nat_set_forget_process)
	(linux_nat_forget_process): New functions.
	* linux-nat.h (linux_nat_iterate_watchpoint_lwps_ftype): Delete
	type.
	(linux_nat_iterate_watchpoint_lwps): Delete declaration.
	(linux_nat_new_fork_ftype, linux_nat_forget_process_ftype): New
	types.
	(linux_nat_set_new_fork, linux_nat_set_forget_process)
	(linux_nat_forget_process): New declarations.

	* amd64fbsd-nat.c (super_mourn_inferior): New global.
	(amd64fbsd_mourn_inferior): New function.
	(_initialize_amd64fbsd_nat): Override to_mourn_inferior.
	* windows-nat.c (windows_detach): Call i386_cleanup_dregs.
This commit is contained in:
Pedro Alves
2013-02-13 14:59:49 +00:00
parent 5befea7261
commit 26cb8b7c1a
10 changed files with 385 additions and 171 deletions

View File

@@ -184,6 +184,13 @@ 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) (struct lwp_info *);
/* The method to call, if any, when a new fork is attached. */
static linux_nat_new_fork_ftype *linux_nat_new_fork;
/* The method to call, if any, when a process is no longer
attached. */
static linux_nat_forget_process_ftype *linux_nat_forget_process_hook;
/* Hook to call prior to resuming a thread. */
static void (*linux_nat_prepare_to_resume) (struct lwp_info *);
@@ -698,15 +705,6 @@ holding the child stopped. Try \"set detach-on-fork\" or \
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);
@@ -1176,12 +1174,22 @@ purge_lwp_list (int pid)
}
}
/* 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). */
/* Add the LWP specified by PTID to the list. PTID is the first LWP
in the process. Return a pointer to the structure describing the
new LWP.
This differs from add_lwp in that we don't let the arch specific
bits know about this new thread. Current clients of this callback
take the opportunity to install watchpoints in the new thread, and
we shouldn't do that for the first thread. If we're spawning a
child ("run"), the thread executes the shell wrapper first, and we
shouldn't touch it until it execs the program we want to debug.
For "attach", it'd be okay to call the callback, but it's not
necessary, because watchpoints can't yet have been inserted into
the inferior. */
static struct lwp_info *
add_lwp (ptid_t ptid)
add_initial_lwp (ptid_t ptid)
{
struct lwp_info *lp;
@@ -1200,15 +1208,25 @@ add_lwp (ptid_t ptid)
lp->next = lwp_list;
lwp_list = lp;
return lp;
}
/* 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. */
static struct lwp_info *
add_lwp (ptid_t ptid)
{
struct lwp_info *lp;
lp = add_initial_lwp (ptid);
/* Let the arch specific bits know about this new thread. Current
clients of this callback take the opportunity to install
watchpoints in the new thread. Don't do this for the first
thread though. If we're spawning a child ("run"), the thread
executes the shell wrapper first, and we shouldn't touch it until
it execs the program we want to debug. For "attach", it'd be
okay to call the callback, but it's not necessary, because
watchpoints can't yet have been inserted into the inferior. */
if (num_lwps (GET_PID (ptid)) > 1 && linux_nat_new_thread != NULL)
watchpoints in the new thread. We don't do this for the first
thread though. See add_initial_lwp. */
if (linux_nat_new_thread != NULL)
linux_nat_new_thread (lp);
return lp;
@@ -1285,45 +1303,6 @@ 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 (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
@@ -1656,7 +1635,7 @@ linux_nat_attach (struct target_ops *ops, char *args, int from_tty)
thread_change_ptid (inferior_ptid, ptid);
/* Add the initial process as the first LWP to the list. */
lp = add_lwp (ptid);
lp = add_initial_lwp (ptid);
status = linux_nat_post_attach_wait (lp->ptid, 1, &lp->cloned,
&lp->signalled);
@@ -2309,6 +2288,15 @@ linux_handle_extended_wait (struct lwp_info *lp, int status,
ourstatus->value.related_pid = ptid_build (new_pid, new_pid, 0);
if (event == PTRACE_EVENT_FORK || event == PTRACE_EVENT_VFORK)
{
/* The arch-specific native code may need to know about new
forks even if those end up never mapped to an
inferior. */
if (linux_nat_new_fork != NULL)
linux_nat_new_fork (lp, new_pid);
}
if (event == PTRACE_EVENT_FORK
&& linux_fork_checkpointing_p (GET_PID (lp->ptid)))
{
@@ -3489,7 +3477,7 @@ linux_nat_wait_1 (struct target_ops *ops,
BUILD_LWP (GET_PID (inferior_ptid),
GET_PID (inferior_ptid)));
lp = add_lwp (inferior_ptid);
lp = add_initial_lwp (inferior_ptid);
lp->resumed = 1;
}
@@ -4075,6 +4063,10 @@ linux_nat_kill (struct target_ops *ops)
{
ptrace (PT_KILL, PIDGET (last.value.related_pid), 0, 0);
wait (&status);
/* Let the arch-specific native code know this process is
gone. */
linux_nat_forget_process (PIDGET (last.value.related_pid));
}
if (forks_exist_p ())
@@ -4103,7 +4095,9 @@ linux_nat_kill (struct target_ops *ops)
static void
linux_nat_mourn_inferior (struct target_ops *ops)
{
purge_lwp_list (ptid_get_pid (inferior_ptid));
int pid = ptid_get_pid (inferior_ptid);
purge_lwp_list (pid);
if (! forks_exist_p ())
/* Normal case, no other forks available. */
@@ -4113,6 +4107,9 @@ linux_nat_mourn_inferior (struct target_ops *ops)
there are other viable forks to debug. Delete the exiting
one and context-switch to the first available. */
linux_fork_mourn_inferior ();
/* Let the arch-specific native code know this process is gone. */
linux_nat_forget_process (pid);
}
/* Convert a native/host siginfo object, into/from the siginfo in the
@@ -5146,6 +5143,35 @@ linux_nat_set_new_thread (struct target_ops *t,
linux_nat_new_thread = new_thread;
}
/* See declaration in linux-nat.h. */
void
linux_nat_set_new_fork (struct target_ops *t,
linux_nat_new_fork_ftype *new_fork)
{
/* Save the pointer. */
linux_nat_new_fork = new_fork;
}
/* See declaration in linux-nat.h. */
void
linux_nat_set_forget_process (struct target_ops *t,
linux_nat_forget_process_ftype *fn)
{
/* Save the pointer. */
linux_nat_forget_process_hook = fn;
}
/* See declaration in linux-nat.h. */
void
linux_nat_forget_process (pid_t pid)
{
if (linux_nat_forget_process_hook != NULL)
linux_nat_forget_process_hook (pid);
}
/* Register a method that converts a siginfo object between the layout
that ptrace returns, and the layout in the architecture of the
inferior. */