[2/4,REPOST] Remote Linux ptrace exec events

Message ID 1398885482-8449-3-git-send-email-donb@codesourcery.com
State New, archived
Headers

Commit Message

Don Breazeal April 30, 2014, 7:18 p.m. UTC
  From: Don Breazeal <don_breazeal@mentor.com>

[Reposting to eliminate unexpected attachment type.]

This patch implements support for exec events in gdbserver on linux, in
multiprocess mode (target extended-remote).  Follow-exec-mode and rerun
behave as expected.  Catchpoints for exec are not yet implemented since it
will be easier to implement catchpoints for fork, vfork, and exec all at
the same time.

TESTING
---------
The patch was tested on GNU/Linux x86_64 with --target_board set to
native-gdbserver and native-extended-gdbserver, as well as testing native
GDB.  The test results for native-gdbserver were unchanged.  Thirteen tests
that used to fail for native-extended-gdbserver on Linux pass with this
patch, and the non-ldr-exc-*.exp tests all pass in non-stop mode and
extended-remote.  There are several failures in the new non-ldr-exc-*.exp
tests in non-stop mode with native GDB.

One caveat: when an exec is detected, gdbserver emits a couple of warnings:
    gdbserver: unexpected r_debug version 0
    gdbserver: unexpected r_debug version 0
However, debugging of shared libraries that are loaded by the exec'd
program works just fine.  These messages may be caused by gdbserver making
an attempt to initialize the solib hook before the r_debug structure has
been initialized.  I intend to follow up in a subsequent patch.

IMPLEMENTATION
----------------
Support for exec events in single-threaded programs was a fairly
straightforward replication of the implementation in native GDB:
   1) Enable exec events via ptrace options.
   2) Add support for handling the exec events to the handle_extended_wait
      and linux_wait_for_event_filtered.  Detect the exec event, then
      find and save the pathname of the executable file being exec'd.
   3) Implement an additional "stop reason", "exec", in the RSP stop reply
      packet "T".
Existing GDB code takes care of handling the exec event on the host side
without modification.

Support for exec events in multi-threaded programs required some additional
work that required a couple of significant changes to existing code.  In a
nutshell, the changes are to:
   4) Use the PTRACE_EVENT_EXIT extended event to handle thread exit,
      while not exposing any change in exit handling to the user.  The
      rationale for this is discussed in the "patch 0" email of this
      series.
   5) Recognize when the exec'ing thread has vanished (become the thread
      group leader) in send_sigstop.  Native GDB does this differently.

Regarding items 4 & 5: Recall that when a non-leader thread exec's, all the
other threads are terminated and the exec'ing thread changes its thread id
to that of the old leader (the process id) as part of the exec.  There is
no event reported for the "exit" of the exec'ing thread; it appears to have
vanished.  The original thread group leader can't be reaped until all the
other threads have been reaped, and some way of determining that it has
exited is required in order to update the lwp list (#4 above). Also, some
mechanism for deleting the lwp entry corresponding to the exec'ing thread
is needed (#5 above).

The rationale for #4 is that in my testing I ran into a race condition in
the mechanism that's intended to detect when a thread group leader has
exited, check_zombie_leaders.  The race occurred when there were only two
threads in the program.  In this case the leader thread passes through a
very brief zombie state before being replaced by the exec'ing thread as
the thread group leader.  This state transition is asynchronous, with no
dependency on anything gdbserver does.  Using PTRACE_EVENT_EXIT ensures
that the leader exit will be detected.  I can provide a more detailed
explanation or the race, but I didn't want to be too long-winded here.

Regarding item #5, determining that the exec'ing thread has "vanished", In
native GDB this is done by calling waitpid(PID), and if it returns ECHILD
it means that the thread is gone.  We don't want to use waitpid(PID) in
gdbserver, based on the discussion in
https://www.sourceware.org/ml/gdb-patches/2014-02/msg00828.html. An
alternative is to send a signal to each thread and look for an ESRCH (No
such process) error.  In all-stop mode this can be done in the normal
course of events, since when gdbserver reports an exec event it stops all
the other threads with a SIGSTOP.  In non-stop mode, when an exec event has
been detected, we can call stop_all_lwps/unstop_all_lwps to accomplish the
same thing.

gdb/
2014-04-02  Don Breazeal  <donb@codesourcery.com>

	* common/linux-ptrace.c (linux_test_for_tracefork)
	[GDBSERVER]: Add exec tracing to ptrace options if OS supports
	it.
	* remote.c (remote_parse_stop_reply): Support new RSP stop
	reply reason 'exec'.

gdbserver/
2014-04-02  Don Breazeal  <donb@codesourcery.com>

	* gdb/gdbserver/linux-low.c (linux_child_pid_to_exec_file): New
	function.
	(handle_extended_wait): Add support for PTRACE_EVENT_EXEC.
	(check_zombie_leaders): Update comment.
	(linux_low_filter_event): Add support for PTRACE_EVENT_EXEC.
	(linux_wait_for_event_filtered): Update comment.
	(extended_event_reported): New function.
	(send_sigstop): Delete lwp on 'No such process' error and
	reset current_inferior.
	* gdb/gdbserver/linux-low.h (struct lwp_info): New member
	'waitstatus'.
	* gdb/gdbserver/remote-utils.c (prepare_resume_reply): Support
	new RSP stop reply reason 'exec'.

---
 gdb/common/linux-ptrace.c    |    7 +-
 gdb/gdbserver/linux-low.c    |  175 ++++++++++++++++++++++++++++++++++++++----
 gdb/gdbserver/linux-low.h    |    5 +
 gdb/gdbserver/remote-utils.c |   28 +++++++-
 gdb/remote.c                 |   27 ++++++-
 5 files changed, 222 insertions(+), 20 deletions(-)
  

Comments

Luis Machado May 7, 2014, 8:01 p.m. UTC | #1
Hi,

On 04/30/2014 04:18 PM, Don Breazeal wrote:
> From: Don Breazeal <don_breazeal@mentor.com>
>
> [Reposting to eliminate unexpected attachment type.]
>
> This patch implements support for exec events in gdbserver on linux, in
> multiprocess mode (target extended-remote).  Follow-exec-mode and rerun
> behave as expected.  Catchpoints for exec are not yet implemented since it
> will be easier to implement catchpoints for fork, vfork, and exec all at
> the same time.
>
> TESTING
> ---------
> The patch was tested on GNU/Linux x86_64 with --target_board set to
> native-gdbserver and native-extended-gdbserver, as well as testing native
> GDB.  The test results for native-gdbserver were unchanged.  Thirteen tests
> that used to fail for native-extended-gdbserver on Linux pass with this
> patch, and the non-ldr-exc-*.exp tests all pass in non-stop mode and
> extended-remote.  There are several failures in the new non-ldr-exc-*.exp
> tests in non-stop mode with native GDB.
>
> One caveat: when an exec is detected, gdbserver emits a couple of warnings:
>      gdbserver: unexpected r_debug version 0
>      gdbserver: unexpected r_debug version 0
> However, debugging of shared libraries that are loaded by the exec'd
> program works just fine.  These messages may be caused by gdbserver making
> an attempt to initialize the solib hook before the r_debug structure has
> been initialized.  I intend to follow up in a subsequent patch.
>
> IMPLEMENTATION
> ----------------
> Support for exec events in single-threaded programs was a fairly
> straightforward replication of the implementation in native GDB:
>     1) Enable exec events via ptrace options.
>     2) Add support for handling the exec events to the handle_extended_wait
>        and linux_wait_for_event_filtered.  Detect the exec event, then
>        find and save the pathname of the executable file being exec'd.
>     3) Implement an additional "stop reason", "exec", in the RSP stop reply
>        packet "T".
> Existing GDB code takes care of handling the exec event on the host side
> without modification.
>
> Support for exec events in multi-threaded programs required some additional
> work that required a couple of significant changes to existing code.  In a
> nutshell, the changes are to:
>     4) Use the PTRACE_EVENT_EXIT extended event to handle thread exit,
>        while not exposing any change in exit handling to the user.  The
>        rationale for this is discussed in the "patch 0" email of this
>        series.
>     5) Recognize when the exec'ing thread has vanished (become the thread
>        group leader) in send_sigstop.  Native GDB does this differently.
>
> Regarding items 4 & 5: Recall that when a non-leader thread exec's, all the
> other threads are terminated and the exec'ing thread changes its thread id
> to that of the old leader (the process id) as part of the exec.  There is
> no event reported for the "exit" of the exec'ing thread; it appears to have
> vanished.  The original thread group leader can't be reaped until all the
> other threads have been reaped, and some way of determining that it has
> exited is required in order to update the lwp list (#4 above). Also, some
> mechanism for deleting the lwp entry corresponding to the exec'ing thread
> is needed (#5 above).
>
> The rationale for #4 is that in my testing I ran into a race condition in
> the mechanism that's intended to detect when a thread group leader has
> exited, check_zombie_leaders.  The race occurred when there were only two
> threads in the program.  In this case the leader thread passes through a
> very brief zombie state before being replaced by the exec'ing thread as
> the thread group leader.  This state transition is asynchronous, with no
> dependency on anything gdbserver does.  Using PTRACE_EVENT_EXIT ensures
> that the leader exit will be detected.  I can provide a more detailed
> explanation or the race, but I didn't want to be too long-winded here.
>
> Regarding item #5, determining that the exec'ing thread has "vanished", In
> native GDB this is done by calling waitpid(PID), and if it returns ECHILD
> it means that the thread is gone.  We don't want to use waitpid(PID) in
> gdbserver, based on the discussion in
> https://www.sourceware.org/ml/gdb-patches/2014-02/msg00828.html. An
> alternative is to send a signal to each thread and look for an ESRCH (No
> such process) error.  In all-stop mode this can be done in the normal
> course of events, since when gdbserver reports an exec event it stops all
> the other threads with a SIGSTOP.  In non-stop mode, when an exec event has
> been detected, we can call stop_all_lwps/unstop_all_lwps to accomplish the
> same thing.
>
> gdb/
> 2014-04-02  Don Breazeal  <donb@codesourcery.com>
>
> 	* common/linux-ptrace.c (linux_test_for_tracefork)
> 	[GDBSERVER]: Add exec tracing to ptrace options if OS supports
> 	it.
> 	* remote.c (remote_parse_stop_reply): Support new RSP stop
> 	reply reason 'exec'.
>
> gdbserver/
> 2014-04-02  Don Breazeal  <donb@codesourcery.com>
>
> 	* gdb/gdbserver/linux-low.c (linux_child_pid_to_exec_file): New
> 	function.
> 	(handle_extended_wait): Add support for PTRACE_EVENT_EXEC.
> 	(check_zombie_leaders): Update comment.
> 	(linux_low_filter_event): Add support for PTRACE_EVENT_EXEC.
> 	(linux_wait_for_event_filtered): Update comment.
> 	(extended_event_reported): New function.
> 	(send_sigstop): Delete lwp on 'No such process' error and
> 	reset current_inferior.
> 	* gdb/gdbserver/linux-low.h (struct lwp_info): New member
> 	'waitstatus'.
> 	* gdb/gdbserver/remote-utils.c (prepare_resume_reply): Support
> 	new RSP stop reply reason 'exec'.
>
> ---
>   gdb/common/linux-ptrace.c    |    7 +-
>   gdb/gdbserver/linux-low.c    |  175 ++++++++++++++++++++++++++++++++++++++----
>   gdb/gdbserver/linux-low.h    |    5 +
>   gdb/gdbserver/remote-utils.c |   28 +++++++-
>   gdb/remote.c                 |   27 ++++++-
>   5 files changed, 222 insertions(+), 20 deletions(-)
>
> diff --git a/gdb/common/linux-ptrace.c b/gdb/common/linux-ptrace.c
> index e3fc705..b137df9 100644
> --- a/gdb/common/linux-ptrace.c
> +++ b/gdb/common/linux-ptrace.c
> @@ -491,8 +491,11 @@ linux_test_for_tracefork (int child_pid)
>     if (ret == child_pid && WIFSTOPPED (status)
>         && status >> 16 == PTRACE_EVENT_EXIT)
>       {
> -        /* PTRACE_O_TRACEEXIT is supported.  */
> -        current_ptrace_options |= PTRACE_O_TRACEEXIT;
> +      /* PTRACE_O_TRACEEXIT is supported.  We use exit events to
> +	 implement support for exec events.  Since fork events are
> +	 supported we know exec events are supported, so we enable
> +	 exec events here.  */
> +      current_ptrace_options |= PTRACE_O_TRACEEXIT | PTRACE_O_TRACEEXEC;
>       }
>   #endif
>   }
> diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
> index 90e7b15..5f94490 100644
> --- a/gdb/gdbserver/linux-low.c
> +++ b/gdb/gdbserver/linux-low.c
> @@ -281,6 +281,32 @@ static int linux_event_pipe[2] = { -1, -1 };
>   static void send_sigstop (struct lwp_info *lwp);
>   static void wait_for_sigstop (void);
>
> +/* Accepts an integer PID.  Returns a string containing the
> +   name of the executable file for the child process.
> +   Space for the result is malloc'd, caller must free.  */
> +
> +static char *
> +linux_child_pid_to_exec_file (int pid)
> +{
> +  char *name1, *name2;
> +
> +  name1 = xmalloc (PATH_MAX);
> +  name2 = xmalloc (PATH_MAX);
> +  memset (name2, 0, PATH_MAX);
> +
> +  sprintf (name1, "/proc/%d/exe", pid);
> +  if (readlink (name1, name2, PATH_MAX) > 0)
> +    {
> +      free (name1);
> +      return name2;
> +    }
> +  else
> +    {
> +      free (name2);
> +      return name1;
> +    }
> +}
> +

At one point in time this function existed in gdbserver/linux-low.c but 
got deleted by the following commit:

commit be07f1a20c962deb662b93209b4ca91bc8e5cbd8
Author: Pedro Alves <palves@redhat.com>
Date:   Fri Jan 27 19:23:43 2012 +0000

     2012-01-27  Pedro Alves  <palves@redhat.com>

         * linux-low.c (linux_child_pid_to_exec_file): Delete.
         (elf_64_file_p): Make static.
         (linux_pid_exe_is_elf_64_file): New.
         * linux-low.h (linux_child_pid_to_exec_file, elf_64_file_p):
         Delete declarations.
         (linux_pid_exe_is_elf_64_file): Declare.
         * linux-x86-low.c (x86_arch_setup): Use
         linux_pid_exe_is_elf_64_file.

It still exists in linux-nat.c though. I remember i tried to unify those 
and move the resulting function to common/, but there may be differences 
preventing such move.

If it can be done, it would be best.

>   /* Return non-zero if HEADER is a 64-bit ELF file.  */
>
>   static int
> @@ -514,6 +540,19 @@ handle_extended_wait (struct lwp_info *event_child, int *wstatp)
>                 (PTRACE_TYPE_ARG4) 0);
>         return ret;
>       }
> +  else if (event == PTRACE_EVENT_EXEC)
> +    {
> +      if (debug_threads)
> +	debug_printf ("LHEW: Got exec event from LWP %ld\n",
> +		      lwpid_of (event_thr));
> +
> +      event_child->waitstatus.kind = TARGET_WAITKIND_EXECD;
> +      event_child->waitstatus.value.execd_pathname
> +	= linux_child_pid_to_exec_file (lwpid_of (event_thr));
> +
> +      /* Report the event.  */
> +      return 0;
> +    }
>     internal_error (__FILE__, __LINE__,
>   	          _("unknown ptrace event %d"), event);
>   }
> @@ -1376,18 +1415,19 @@ check_zombie_leaders (void)
>   	     program).  In the latter case, we can't waitpid the
>   	     leader's exit status until all other threads are gone.
>
> -	     - There are 3 or more threads in the group, and a thread
> +	     - There are multiple threads in the group, and a thread
>   	     other than the leader exec'd.  On an exec, the Linux
>   	     kernel destroys all other threads (except the execing
>   	     one) in the thread group, and resets the execing thread's
>   	     tid to the tgid.  No exit notification is sent for the
>   	     execing thread -- from the ptracer's perspective, it
>   	     appears as though the execing thread just vanishes.
> -	     Until we reap all other threads except the leader and the
> -	     execing thread, the leader will be zombie, and the
> -	     execing thread will be in `D (disc sleep)'.  As soon as
> -	     all other threads are reaped, the execing thread changes
> -	     it's tid to the tgid, and the previous (zombie) leader
> +	     Until we reap all other threads (if any) except the
> +	     leader and the execing thread, the leader will be zombie,
> +	     and the execing thread will be in `D (disc sleep)'.  As
> +	     soon as all other threads are reaped, or have reported
> +	     PTRACE_EVENT_EXIT events, the execing thread changes its
> +	     tid to the tgid, and the previous (zombie) leader
>   	     vanishes, giving place to the "new" leader.  We could try
>   	     distinguishing the exit and exec cases, by waiting once
>   	     more, and seeing if something comes out, but it doesn't
> @@ -1395,7 +1435,11 @@ check_zombie_leaders (void)
>   	     we'll re-add the new one once we see the exec event
>   	     (which is just the same as what would happen if the
>   	     previous leader did exit voluntarily before some other
> -	     thread execs).  */
> +	     thread execs).
> +
> +	     Note that when PTRACE_EVENT_EXEC is supported, we use
> +	     that mechanism to detect thread exit, including the
> +	     exit of zombie leaders.  */
>
>   	  if (debug_threads)
>   	    fprintf (stderr,
> @@ -1791,6 +1835,57 @@ linux_low_filter_event (ptid_t filter_ptid, int lwpid, int *wstatp)
>
>     child = find_lwp_pid (pid_to_ptid (lwpid));
>
> +  /* Check for stop events reported by a process we didn't already
> +     know about - anything not already in our LWP list.
> +
> +     If we're expecting to receive stopped processes after
> +     fork, vfork, and clone events, then we'll just add the
> +     new one to our list and go back to waiting for the event
> +     to be reported - the stopped process might be returned
> +     from waitpid before or after the event is.
> +
> +     But note the case of a non-leader thread exec'ing after the
> +     leader having exited, and gone from our lists.  On an exec,
> +     the Linux kernel destroys all other threads (except the execing
> +     one) in the thread group, and resets the execing thread's tid
> +     to the tgid.  No exit notification is sent for the execing
> +     thread -- from the ptracer's perspective, it appears as though
> +     the execing thread just vanishes.  When they are available, we
> +     use exit events (PTRACE_EVENT_EXIT) to detect thread exit
> +     reliably.  As soon as all other threads (if any) are reaped or
> +     have reported their PTRACE_EVENT_EXIT events, the execing
> +     thread changes it's tid to the tgid, and the previous (zombie)
> +     leader vanishes, giving place to the "new" leader.  The lwp
> +     entry for the previous leader is deleted when we handle its
> +     exit event, and we re-add the new one here.  */
> +
> +  if (WIFSTOPPED (wstat) && child == NULL
> +      && (WSTOPSIG (wstat) == SIGTRAP && wstat >> 16 == PTRACE_EVENT_EXEC))
> +    {
> +      ptid_t child_ptid;
> +

It would be nice to replace the shift operation with a predicate to test 
for PTRACE_EVENT_EXEC, just like you did for PTRACE_EVENT_EXIT.

> +      /* A multi-thread exec after we had seen the leader exiting.  */
> +      if (debug_threads)
> +	debug_printf ("LLW: Re-adding thread group leader LWP %d.\n",
> +		      lwpid);
> +
> +      child_ptid = ptid_build (lwpid, lwpid, 0);
> +      child = add_lwp (child_ptid);
> +      child->stopped = 1;
> +      current_inferior = child->thread;
> +
> +      if (non_stop && stopping_threads == NOT_STOPPING_THREADS)
> +	{
> +	  /* Make sure we delete the lwp entry for the exec'ing thread,
> +	     which will have vanished.  We do this by sending a signal
> +	     to all the other threads in the lwp list, deleting any
> +	     that are not found.  Note that in all-stop mode this will
> +	     happen before reporting the event.  */
> +	  stop_all_lwps (0, child);
> +	  unstop_all_lwps (0, child);
> +	}
> +    }
> +
>     /* If we didn't find a process, one of two things presumably happened:
>        - A process we started and then detached from has exited.  Ignore it.
>        - A process we are controlling has forked and the new child's stop
> @@ -2122,8 +2217,7 @@ linux_wait_for_event_filtered (ptid_t wait_ptid, ptid_t filter_ptid,
>   	 - When a non-leader thread execs, that thread just vanishes
>   	   without reporting an exit (so we'd hang if we waited for it
>   	   explicitly in that case).  The exec event is reported to
> -	   the TGID pid (although we don't currently enable exec
> -	   events).  */
> +	   the TGID pid.  */
>         errno = 0;
>         ret = my_waitpid (-1, wstatp, options | WNOHANG);
>
> @@ -2520,6 +2614,21 @@ linux_stabilize_threads (void)
>       }
>   }
>
> +/* Return non-zero if WAITSTATUS reflects an extended linux
> +   event.  Otherwise, return 0.  Note that extended EXIT
> +   events are fixed up and handled like normal events, so
> +   they are not considered here.  */
> +
> +static int
> +extended_event_reported (const struct target_waitstatus *waitstatus)
> +{
> +
> +  if (waitstatus == NULL)
> +    return 0;
> +
> +  return waitstatus->kind == TARGET_WAITKIND_EXECD;
> +}
> +

Someone mentioned putting ()'s around the condition for better 
readability. Though not required, i also agree that it improves things.

Not a requirement, just a suggestion.

>   /* Wait for process, returns status.  */
>
>   static ptid_t
> @@ -2883,7 +2992,8 @@ retry:
>   		       && !bp_explains_trap && !trace_event)
>   		   || (gdb_breakpoint_here (event_child->stop_pc)
>   		       && gdb_condition_true_at_breakpoint (event_child->stop_pc)
> -		       && gdb_no_commands_at_breakpoint (event_child->stop_pc)));
> +		       && gdb_no_commands_at_breakpoint (event_child->stop_pc))
> +		   || extended_event_reported (&event_child->waitstatus));
>
>     run_breakpoint_commands (event_child->stop_pc);
>
> @@ -2905,6 +3015,15 @@ retry:
>   			  paddress (event_child->stop_pc),
>   			  paddress (event_child->step_range_start),
>   			  paddress (event_child->step_range_end));
> +	  if (debug_threads
> +	      && extended_event_reported (&event_child->waitstatus))
> +	    {
> +	      char *str = target_waitstatus_to_string (ourstatus);
> +	      debug_printf ("LWP %ld has forked, cloned, vforked or execd"
> +			    " with waitstatus %s\n",
> +			    lwpid_of (get_lwp_thread (event_child)), str);
> +	      xfree (str);
> +	    }
>   	}
>

Maybe we could provide more precise information about the event here 
instead of something generic? It may help debugging in the future or if 
we ever have a mix of these events happening very close to each other.

>         /* We're not reporting this breakpoint to GDB, so apply the
> @@ -3003,7 +3122,19 @@ retry:
>   	unstop_all_lwps (1, event_child);
>       }
>
> -  ourstatus->kind = TARGET_WAITKIND_STOPPED;
> +  if (extended_event_reported (&event_child->waitstatus))
> +    {
> +      /* If the reported event is a fork, vfork or exec, let GDB
> +	 know.  */
> +      ourstatus->kind = event_child->waitstatus.kind;
> +      ourstatus->value = event_child->waitstatus.value;
> +
> +      /* Reset the event child's waitstatus since we handled it
> +	 already.  */
> +      event_child->waitstatus.kind = TARGET_WAITKIND_IGNORE;
> +    }
> +  else
> +    ourstatus->kind = TARGET_WAITKIND_STOPPED;
>

This chunk of code setting waitstatus.kind to TARGET_WAITKIND_IGNORE may 
be a bit confusing/hackish, but we really don't want gdbserver's event 
loop to handle the extended events as TARGET_WAITKIND_STOPPED.

gdbserver's event loop could probably use a cleanup . For the time 
being, this looks good to me though.

>     if (current_inferior->last_resume_kind == resume_stop
>         && WSTOPSIG (w) == SIGSTOP)
> @@ -3014,13 +3145,14 @@ retry:
>         ourstatus->value.sig = GDB_SIGNAL_0;
>       }
>     else if (current_inferior->last_resume_kind == resume_stop
> -	   && WSTOPSIG (w) != SIGSTOP)
> +	   && WSTOPSIG (w) != SIGSTOP
> +	   && !extended_event_reported (ourstatus))
>       {
>         /* A thread that has been requested to stop by GDB with vCont;t,
>   	 but, it stopped for other reasons.  */
>         ourstatus->value.sig = gdb_signal_from_host (WSTOPSIG (w));
>       }
> -  else
> +  else if (ourstatus->kind == TARGET_WAITKIND_STOPPED)
>       {
>         ourstatus->value.sig = gdb_signal_from_host (WSTOPSIG (w));
>       }
> @@ -3126,6 +3258,7 @@ static void
>   send_sigstop (struct lwp_info *lwp)
>   {
>     int pid;
> +  int ret;
>
>     pid = lwpid_of (get_lwp_thread (lwp));
>
> @@ -3143,7 +3276,21 @@ send_sigstop (struct lwp_info *lwp)
>       debug_printf ("Sending sigstop to lwp %d\n", pid);
>
>     lwp->stop_expected = 1;
> -  kill_lwp (pid, SIGSTOP);
> +  errno = 0;
> +  ret = kill_lwp (pid, SIGSTOP);
> +  if (ret == -1 && errno == ESRCH)
> +    {
> +      /* If the kill fails with "No such process", on GNU/Linux we know
> +	 that the LWP has vanished - it is not a zombie, it is gone.
> +	 This is due to a thread other than the thread group leader
> +	 calling exec.  See comments in linux_low_filter_event regarding
> +	 PTRACE_EVENT_EXEC.  */
> +      delete_lwp (lwp);
> +      set_desired_inferior (0);
> +
> +      if (debug_threads)
> +	debug_printf ("send_sigstop: lwp %d has vanished\n", pid);
> +    }
>   }
>
>   static int
> diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h
> index 7459710..7759f01 100644
> --- a/gdb/gdbserver/linux-low.h
> +++ b/gdb/gdbserver/linux-low.h
> @@ -265,6 +265,11 @@ struct lwp_info
>     /* When stopped is set, the last wait status recorded for this lwp.  */
>     int last_status;
>
> +  /* If WAITSTATUS->KIND != TARGET_WAITKIND_IGNORE, the waitstatus for
> +     this LWP's last event.  This may correspond to LAST_STATUS above,
> +     or to the current status during event processing.  */
> +  struct target_waitstatus waitstatus;
> +
>     /* When stopped is set, this is where the lwp stopped, with
>        decr_pc_after_break already accounted for.  */
>     CORE_ADDR stop_pc;
> diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c
> index 4fcafa0..9ce25dc 100644
> --- a/gdb/gdbserver/remote-utils.c
> +++ b/gdb/gdbserver/remote-utils.c
> @@ -1111,14 +1111,40 @@ prepare_resume_reply (char *buf, ptid_t ptid,
>     switch (status->kind)
>       {
>       case TARGET_WAITKIND_STOPPED:
> +    case TARGET_WAITKIND_EXECD:
>         {
>   	struct thread_info *saved_inferior;
>   	const char **regp;
>   	struct regcache *regcache;
> +	enum gdb_signal signal;
> +
> +	if (status->kind == TARGET_WAITKIND_EXECD)
> +	  signal = GDB_SIGNAL_TRAP;
> +	else
> +	  signal = status->value.sig;
> +
> +	sprintf (buf, "T%02x", signal);
>
> -	sprintf (buf, "T%02x", status->value.sig);
>   	buf += strlen (buf);
>
> +	if (status->kind == TARGET_WAITKIND_EXECD && multi_process)
> +	  {
> +	    const char *event = "exec";
> +	    char hexified_pathname[PATH_MAX];
> +
> +	    sprintf (buf, "%s:", event);
> +	    buf += strlen (buf);
> +
> +	    /* Encode pathname to hexified format.  */
> +	    bin2hex ((const gdb_byte *) status->value.execd_pathname,
> +		     hexified_pathname, strlen(status->value.execd_pathname));
> +
> +	    sprintf (buf, "%s;", hexified_pathname);
> +	    xfree (status->value.execd_pathname);
> +	    status->value.execd_pathname = NULL;
> +	    buf += strlen (buf);
> +	  }
> +
>   	saved_inferior = current_inferior;
>
>   	current_inferior = find_thread_ptid (ptid);
> diff --git a/gdb/remote.c b/gdb/remote.c
> index be8c423..f4412d8 100644
> --- a/gdb/remote.c
> +++ b/gdb/remote.c
> @@ -5542,11 +5542,13 @@ remote_parse_stop_reply (char *buf, struct stop_reply *event)
>   	     pnum and set p1 to point to the character following it.
>   	     Otherwise p1 points to p.  */
>
> -	  /* If this packet is an awatch packet, don't parse the 'a'
> -	     as a register number.  */
> +	  /* If this packet has a stop reason string that starts
> +	     with a character that could be a hex digit, don't parse
> +	     it as a register number.  */
>
>   	  if (strncmp (p, "awatch", strlen("awatch")) != 0
> -	      && strncmp (p, "core", strlen ("core") != 0))
> +	      && strncmp (p, "core", strlen ("core") != 0)
> +	      && strncmp (p, "exec", strlen ("exec") != 0))
>   	    {
>   	      /* Read the ``P'' register number.  */
>   	      pnum = strtol (p, &p_temp, 16);
> @@ -5598,6 +5600,25 @@ Packet: '%s'\n"),
>   		  p = unpack_varlen_hex (++p1, &c);
>   		  event->core = c;
>   		}
> +	      else if (strncmp (p, "exec", p1 - p) == 0)
> +		{
> +		  ULONGEST pid;
> +		  char pathname[PATH_MAX];
> +
> +		  p = unpack_varlen_hex (++p1, &pid);
> +
> +		  /* Save the pathname for event reporting and for
> +		     the next run command. */
> +		  hex2bin (p1, (gdb_byte *) pathname, (p - p1)/2);
> +		  /* Add the null terminator.  */
> +		  pathname[(p - p1)/2] = '\0';
> +		  /* This is freed during event handling.  */
> +		  event->ws.value.execd_pathname = xstrdup (pathname);
> +		  event->ws.kind = TARGET_WAITKIND_EXECD;
> +		  /* Save the pathname for the next run command.  */
> +		  xfree (remote_exec_file);
> +		  remote_exec_file = pathname;
> +		}
>   	      else
>   		{
>   		  /* Silently skip unknown optional info.  */
>
  
Yao Qi May 8, 2014, 5:41 a.m. UTC | #2
I don't have much based on Luis's comments.  Two nits on changelog:

On 05/01/2014 03:18 AM, Don Breazeal wrote:
> gdbserver/
> 2014-04-02  Don Breazeal  <donb@codesourcery.com>
> 
> 	* gdb/gdbserver/linux-low.c (linux_child_pid_to_exec_file): New
	  ^^^^^^^^^^^^^

There is a ChangeLog file in gdb/gdbserver directory, so don't need to
have "gdb/gdbserver/" in file name. "* linux-low.c (...." should be fine.

> 	function.
> 	(handle_extended_wait): Add support for PTRACE_EVENT_EXEC.
> 	(check_zombie_leaders): Update comment.
> 	(linux_low_filter_event): Add support for PTRACE_EVENT_EXEC.
> 	(linux_wait_for_event_filtered): Update comment.
> 	(extended_event_reported): New function.
> 	(send_sigstop): Delete lwp on 'No such process' error and
> 	reset current_inferior.
> 	* gdb/gdbserver/linux-low.h (struct lwp_info): New member
> 	'waitstatus'.

We document new member with angle brackets, like

	* linux-low.h (struct lwp_info) <waitstatus>: New member.
  
Don Breazeal May 9, 2014, 9:17 p.m. UTC | #3
Hi Luis,
Thanks for the review.

On 5/7/2014 1:01 PM, Luis Machado wrote:
> Hi,
>
> On 04/30/2014 04:18 PM, Don Breazeal wrote:
>> From: Don Breazeal <don_breazeal@mentor.com>
>>
>> [Reposting to eliminate unexpected attachment type.]
>>
>> This patch implements support for exec events in gdbserver on linux, in
>> multiprocess mode (target extended-remote).  Follow-exec-mode and rerun
>> behave as expected.  Catchpoints for exec are not yet implemented
>> since it
>> will be easier to implement catchpoints for fork, vfork, and exec all at
>> the same time.
>>
>> TESTING
>> ---------
>> The patch was tested on GNU/Linux x86_64 with --target_board set to
>> native-gdbserver and native-extended-gdbserver, as well as testing native
>> GDB.  The test results for native-gdbserver were unchanged.  Thirteen
>> tests
>> that used to fail for native-extended-gdbserver on Linux pass with this
>> patch, and the non-ldr-exc-*.exp tests all pass in non-stop mode and
>> extended-remote.  There are several failures in the new non-ldr-exc-*.exp
>> tests in non-stop mode with native GDB.
>>
>> One caveat: when an exec is detected, gdbserver emits a couple of
>> warnings:
>>      gdbserver: unexpected r_debug version 0
>>      gdbserver: unexpected r_debug version 0
>> However, debugging of shared libraries that are loaded by the exec'd
>> program works just fine.  These messages may be caused by gdbserver
>> making
>> an attempt to initialize the solib hook before the r_debug structure has
>> been initialized.  I intend to follow up in a subsequent patch.
>>
>> IMPLEMENTATION
>> ----------------
>> Support for exec events in single-threaded programs was a fairly
>> straightforward replication of the implementation in native GDB:
>>     1) Enable exec events via ptrace options.
>>     2) Add support for handling the exec events to the
>> handle_extended_wait
>>        and linux_wait_for_event_filtered.  Detect the exec event, then
>>        find and save the pathname of the executable file being exec'd.
>>     3) Implement an additional "stop reason", "exec", in the RSP stop
>> reply
>>        packet "T".
>> Existing GDB code takes care of handling the exec event on the host side
>> without modification.
>>
>> Support for exec events in multi-threaded programs required some
>> additional
>> work that required a couple of significant changes to existing code.
>> In a
>> nutshell, the changes are to:
>>     4) Use the PTRACE_EVENT_EXIT extended event to handle thread exit,
>>        while not exposing any change in exit handling to the user.  The
>>        rationale for this is discussed in the "patch 0" email of this
>>        series.
>>     5) Recognize when the exec'ing thread has vanished (become the thread
>>        group leader) in send_sigstop.  Native GDB does this differently.
>>
>> Regarding items 4 & 5: Recall that when a non-leader thread exec's,
>> all the
>> other threads are terminated and the exec'ing thread changes its
>> thread id
>> to that of the old leader (the process id) as part of the exec.  There is
>> no event reported for the "exit" of the exec'ing thread; it appears to
>> have
>> vanished.  The original thread group leader can't be reaped until all the
>> other threads have been reaped, and some way of determining that it has
>> exited is required in order to update the lwp list (#4 above). Also, some
>> mechanism for deleting the lwp entry corresponding to the exec'ing thread
>> is needed (#5 above).
>>
>> The rationale for #4 is that in my testing I ran into a race condition in
>> the mechanism that's intended to detect when a thread group leader has
>> exited, check_zombie_leaders.  The race occurred when there were only two
>> threads in the program.  In this case the leader thread passes through a
>> very brief zombie state before being replaced by the exec'ing thread as
>> the thread group leader.  This state transition is asynchronous, with no
>> dependency on anything gdbserver does.  Using PTRACE_EVENT_EXIT ensures
>> that the leader exit will be detected.  I can provide a more detailed
>> explanation or the race, but I didn't want to be too long-winded here.
>>
>> Regarding item #5, determining that the exec'ing thread has
>> "vanished", In
>> native GDB this is done by calling waitpid(PID), and if it returns ECHILD
>> it means that the thread is gone.  We don't want to use waitpid(PID) in
>> gdbserver, based on the discussion in
>> https://www.sourceware.org/ml/gdb-patches/2014-02/msg00828.html. An
>> alternative is to send a signal to each thread and look for an ESRCH (No
>> such process) error.  In all-stop mode this can be done in the normal
>> course of events, since when gdbserver reports an exec event it stops all
>> the other threads with a SIGSTOP.  In non-stop mode, when an exec
>> event has
>> been detected, we can call stop_all_lwps/unstop_all_lwps to accomplish
>> the
>> same thing.
>>
>> gdb/
>> 2014-04-02  Don Breazeal  <donb@codesourcery.com>
>>
>>     * common/linux-ptrace.c (linux_test_for_tracefork)
>>     [GDBSERVER]: Add exec tracing to ptrace options if OS supports
>>     it.
>>     * remote.c (remote_parse_stop_reply): Support new RSP stop
>>     reply reason 'exec'.
>>
>> gdbserver/
>> 2014-04-02  Don Breazeal  <donb@codesourcery.com>
>>
>>     * gdb/gdbserver/linux-low.c (linux_child_pid_to_exec_file): New
>>     function.
>>     (handle_extended_wait): Add support for PTRACE_EVENT_EXEC.
>>     (check_zombie_leaders): Update comment.
>>     (linux_low_filter_event): Add support for PTRACE_EVENT_EXEC.
>>     (linux_wait_for_event_filtered): Update comment.
>>     (extended_event_reported): New function.
>>     (send_sigstop): Delete lwp on 'No such process' error and
>>     reset current_inferior.
>>     * gdb/gdbserver/linux-low.h (struct lwp_info): New member
>>     'waitstatus'.
>>     * gdb/gdbserver/remote-utils.c (prepare_resume_reply): Support
>>     new RSP stop reply reason 'exec'.
>>
>> ---
>>   gdb/common/linux-ptrace.c    |    7 +-
>>   gdb/gdbserver/linux-low.c    |  175
>> ++++++++++++++++++++++++++++++++++++++----
>>   gdb/gdbserver/linux-low.h    |    5 +
>>   gdb/gdbserver/remote-utils.c |   28 +++++++-
>>   gdb/remote.c                 |   27 ++++++-
>>   5 files changed, 222 insertions(+), 20 deletions(-)
>>
>> diff --git a/gdb/common/linux-ptrace.c b/gdb/common/linux-ptrace.c
>> index e3fc705..b137df9 100644
>> --- a/gdb/common/linux-ptrace.c
>> +++ b/gdb/common/linux-ptrace.c
>> @@ -491,8 +491,11 @@ linux_test_for_tracefork (int child_pid)
>>     if (ret == child_pid && WIFSTOPPED (status)
>>         && status >> 16 == PTRACE_EVENT_EXIT)
>>       {
>> -        /* PTRACE_O_TRACEEXIT is supported.  */
>> -        current_ptrace_options |= PTRACE_O_TRACEEXIT;
>> +      /* PTRACE_O_TRACEEXIT is supported.  We use exit events to
>> +     implement support for exec events.  Since fork events are
>> +     supported we know exec events are supported, so we enable
>> +     exec events here.  */
>> +      current_ptrace_options |= PTRACE_O_TRACEEXIT | PTRACE_O_TRACEEXEC;
>>       }
>>   #endif
>>   }
>> diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
>> index 90e7b15..5f94490 100644
>> --- a/gdb/gdbserver/linux-low.c
>> +++ b/gdb/gdbserver/linux-low.c
>> @@ -281,6 +281,32 @@ static int linux_event_pipe[2] = { -1, -1 };
>>   static void send_sigstop (struct lwp_info *lwp);
>>   static void wait_for_sigstop (void);
>>
>> +/* Accepts an integer PID.  Returns a string containing the
>> +   name of the executable file for the child process.
>> +   Space for the result is malloc'd, caller must free.  */
>> +
>> +static char *
>> +linux_child_pid_to_exec_file (int pid)
>> +{
>> +  char *name1, *name2;
>> +
>> +  name1 = xmalloc (PATH_MAX);
>> +  name2 = xmalloc (PATH_MAX);
>> +  memset (name2, 0, PATH_MAX);
>> +
>> +  sprintf (name1, "/proc/%d/exe", pid);
>> +  if (readlink (name1, name2, PATH_MAX) > 0)
>> +    {
>> +      free (name1);
>> +      return name2;
>> +    }
>> +  else
>> +    {
>> +      free (name2);
>> +      return name1;
>> +    }
>> +}
>> +
>
> At one point in time this function existed in gdbserver/linux-low.c but
> got deleted by the following commit:
>
> commit be07f1a20c962deb662b93209b4ca91bc8e5cbd8
> Author: Pedro Alves <palves@redhat.com>
> Date:   Fri Jan 27 19:23:43 2012 +0000
>
>      2012-01-27  Pedro Alves  <palves@redhat.com>
>
>          * linux-low.c (linux_child_pid_to_exec_file): Delete.
>          (elf_64_file_p): Make static.
>          (linux_pid_exe_is_elf_64_file): New.
>          * linux-low.h (linux_child_pid_to_exec_file, elf_64_file_p):
>          Delete declarations.
>          (linux_pid_exe_is_elf_64_file): Declare.
>          * linux-x86-low.c (x86_arch_setup): Use
>          linux_pid_exe_is_elf_64_file.
>
> It still exists in linux-nat.c though. I remember i tried to unify those
> and move the resulting function to common/, but there may be differences
> preventing such move.
>
> If it can be done, it would be best.

I am working on doing this.  It seems like it should be possible.  One 
question: the version of the function in linux-nat.c takes a "struct 
target_ops *" argument, I assume because it is used in the target_ops 
function vector.  Is it acceptable to include "target.h" in a common 
file?  It will pick up different files for GDB and GDBserver.  I'm not 
sure if that would violate some aspect of the design.  common/btrace.c 
includes it, but it's the only common file that does.

>
>>   /* Return non-zero if HEADER is a 64-bit ELF file.  */
>>
>>   static int
>> @@ -514,6 +540,19 @@ handle_extended_wait (struct lwp_info
>> *event_child, int *wstatp)
>>                 (PTRACE_TYPE_ARG4) 0);
>>         return ret;
>>       }
>> +  else if (event == PTRACE_EVENT_EXEC)
>> +    {
>> +      if (debug_threads)
>> +    debug_printf ("LHEW: Got exec event from LWP %ld\n",
>> +              lwpid_of (event_thr));
>> +
>> +      event_child->waitstatus.kind = TARGET_WAITKIND_EXECD;
>> +      event_child->waitstatus.value.execd_pathname
>> +    = linux_child_pid_to_exec_file (lwpid_of (event_thr));
>> +
>> +      /* Report the event.  */
>> +      return 0;
>> +    }
>>     internal_error (__FILE__, __LINE__,
>>                 _("unknown ptrace event %d"), event);
>>   }
>> @@ -1376,18 +1415,19 @@ check_zombie_leaders (void)
>>            program).  In the latter case, we can't waitpid the
>>            leader's exit status until all other threads are gone.
>>
>> -         - There are 3 or more threads in the group, and a thread
>> +         - There are multiple threads in the group, and a thread
>>            other than the leader exec'd.  On an exec, the Linux
>>            kernel destroys all other threads (except the execing
>>            one) in the thread group, and resets the execing thread's
>>            tid to the tgid.  No exit notification is sent for the
>>            execing thread -- from the ptracer's perspective, it
>>            appears as though the execing thread just vanishes.
>> -         Until we reap all other threads except the leader and the
>> -         execing thread, the leader will be zombie, and the
>> -         execing thread will be in `D (disc sleep)'.  As soon as
>> -         all other threads are reaped, the execing thread changes
>> -         it's tid to the tgid, and the previous (zombie) leader
>> +         Until we reap all other threads (if any) except the
>> +         leader and the execing thread, the leader will be zombie,
>> +         and the execing thread will be in `D (disc sleep)'.  As
>> +         soon as all other threads are reaped, or have reported
>> +         PTRACE_EVENT_EXIT events, the execing thread changes its
>> +         tid to the tgid, and the previous (zombie) leader
>>            vanishes, giving place to the "new" leader.  We could try
>>            distinguishing the exit and exec cases, by waiting once
>>            more, and seeing if something comes out, but it doesn't
>> @@ -1395,7 +1435,11 @@ check_zombie_leaders (void)
>>            we'll re-add the new one once we see the exec event
>>            (which is just the same as what would happen if the
>>            previous leader did exit voluntarily before some other
>> -         thread execs).  */
>> +         thread execs).
>> +
>> +         Note that when PTRACE_EVENT_EXEC is supported, we use
>> +         that mechanism to detect thread exit, including the
>> +         exit of zombie leaders.  */
>>
>>         if (debug_threads)
>>           fprintf (stderr,
>> @@ -1791,6 +1835,57 @@ linux_low_filter_event (ptid_t filter_ptid, int
>> lwpid, int *wstatp)
>>
>>     child = find_lwp_pid (pid_to_ptid (lwpid));
>>
>> +  /* Check for stop events reported by a process we didn't already
>> +     know about - anything not already in our LWP list.
>> +
>> +     If we're expecting to receive stopped processes after
>> +     fork, vfork, and clone events, then we'll just add the
>> +     new one to our list and go back to waiting for the event
>> +     to be reported - the stopped process might be returned
>> +     from waitpid before or after the event is.
>> +
>> +     But note the case of a non-leader thread exec'ing after the
>> +     leader having exited, and gone from our lists.  On an exec,
>> +     the Linux kernel destroys all other threads (except the execing
>> +     one) in the thread group, and resets the execing thread's tid
>> +     to the tgid.  No exit notification is sent for the execing
>> +     thread -- from the ptracer's perspective, it appears as though
>> +     the execing thread just vanishes.  When they are available, we
>> +     use exit events (PTRACE_EVENT_EXIT) to detect thread exit
>> +     reliably.  As soon as all other threads (if any) are reaped or
>> +     have reported their PTRACE_EVENT_EXIT events, the execing
>> +     thread changes it's tid to the tgid, and the previous (zombie)
>> +     leader vanishes, giving place to the "new" leader.  The lwp
>> +     entry for the previous leader is deleted when we handle its
>> +     exit event, and we re-add the new one here.  */
>> +
>> +  if (WIFSTOPPED (wstat) && child == NULL
>> +      && (WSTOPSIG (wstat) == SIGTRAP && wstat >> 16 ==
>> PTRACE_EVENT_EXEC))
>> +    {
>> +      ptid_t child_ptid;
>> +
>
> It would be nice to replace the shift operation with a predicate to test
> for PTRACE_EVENT_EXEC, just like you did for PTRACE_EVENT_EXIT.

Will do.  Actually, given your previous comment, I intend to move all of 
these predicates into common/linux-ptrace.[ch].

>
>> +      /* A multi-thread exec after we had seen the leader exiting.  */
>> +      if (debug_threads)
>> +    debug_printf ("LLW: Re-adding thread group leader LWP %d.\n",
>> +              lwpid);
>> +
>> +      child_ptid = ptid_build (lwpid, lwpid, 0);
>> +      child = add_lwp (child_ptid);
>> +      child->stopped = 1;
>> +      current_inferior = child->thread;
>> +
>> +      if (non_stop && stopping_threads == NOT_STOPPING_THREADS)
>> +    {
>> +      /* Make sure we delete the lwp entry for the exec'ing thread,
>> +         which will have vanished.  We do this by sending a signal
>> +         to all the other threads in the lwp list, deleting any
>> +         that are not found.  Note that in all-stop mode this will
>> +         happen before reporting the event.  */
>> +      stop_all_lwps (0, child);
>> +      unstop_all_lwps (0, child);
>> +    }
>> +    }
>> +
>>     /* If we didn't find a process, one of two things presumably
>> happened:
>>        - A process we started and then detached from has exited.
>> Ignore it.
>>        - A process we are controlling has forked and the new child's stop
>> @@ -2122,8 +2217,7 @@ linux_wait_for_event_filtered (ptid_t wait_ptid,
>> ptid_t filter_ptid,
>>        - When a non-leader thread execs, that thread just vanishes
>>          without reporting an exit (so we'd hang if we waited for it
>>          explicitly in that case).  The exec event is reported to
>> -       the TGID pid (although we don't currently enable exec
>> -       events).  */
>> +       the TGID pid.  */
>>         errno = 0;
>>         ret = my_waitpid (-1, wstatp, options | WNOHANG);
>>
>> @@ -2520,6 +2614,21 @@ linux_stabilize_threads (void)
>>       }
>>   }
>>
>> +/* Return non-zero if WAITSTATUS reflects an extended linux
>> +   event.  Otherwise, return 0.  Note that extended EXIT
>> +   events are fixed up and handled like normal events, so
>> +   they are not considered here.  */
>> +
>> +static int
>> +extended_event_reported (const struct target_waitstatus *waitstatus)
>> +{
>> +
>> +  if (waitstatus == NULL)
>> +    return 0;
>> +
>> +  return waitstatus->kind == TARGET_WAITKIND_EXECD;
>> +}
>> +
>
> Someone mentioned putting ()'s around the condition for better
> readability. Though not required, i also agree that it improves things.
>
> Not a requirement, just a suggestion.

I'll do it.

>
>>   /* Wait for process, returns status.  */
>>
>>   static ptid_t
>> @@ -2883,7 +2992,8 @@ retry:
>>                  && !bp_explains_trap && !trace_event)
>>              || (gdb_breakpoint_here (event_child->stop_pc)
>>                  && gdb_condition_true_at_breakpoint
>> (event_child->stop_pc)
>> -               && gdb_no_commands_at_breakpoint
>> (event_child->stop_pc)));
>> +               && gdb_no_commands_at_breakpoint (event_child->stop_pc))
>> +           || extended_event_reported (&event_child->waitstatus));
>>
>>     run_breakpoint_commands (event_child->stop_pc);
>>
>> @@ -2905,6 +3015,15 @@ retry:
>>                 paddress (event_child->stop_pc),
>>                 paddress (event_child->step_range_start),
>>                 paddress (event_child->step_range_end));
>> +      if (debug_threads
>> +          && extended_event_reported (&event_child->waitstatus))
>> +        {
>> +          char *str = target_waitstatus_to_string (ourstatus);
>> +          debug_printf ("LWP %ld has forked, cloned, vforked or execd"
>> +                " with waitstatus %s\n",
>> +                lwpid_of (get_lwp_thread (event_child)), str);
>> +          xfree (str);
>> +        }
>>       }
>>
>
> Maybe we could provide more precise information about the event here
> instead of something generic? It may help debugging in the future or if
> we ever have a mix of these events happening very close to each other.

It turns out the string from target_waitstatus_to_string contains the 
precise information.  I'll clean this up.

>
>>         /* We're not reporting this breakpoint to GDB, so apply the
>> @@ -3003,7 +3122,19 @@ retry:
>>       unstop_all_lwps (1, event_child);
>>       }
>>
>> -  ourstatus->kind = TARGET_WAITKIND_STOPPED;
>> +  if (extended_event_reported (&event_child->waitstatus))
>> +    {
>> +      /* If the reported event is a fork, vfork or exec, let GDB
>> +     know.  */
>> +      ourstatus->kind = event_child->waitstatus.kind;
>> +      ourstatus->value = event_child->waitstatus.value;
>> +
>> +      /* Reset the event child's waitstatus since we handled it
>> +     already.  */
>> +      event_child->waitstatus.kind = TARGET_WAITKIND_IGNORE;
>> +    }
>> +  else
>> +    ourstatus->kind = TARGET_WAITKIND_STOPPED;
>>
>
> This chunk of code setting waitstatus.kind to TARGET_WAITKIND_IGNORE may
> be a bit confusing/hackish, but we really don't want gdbserver's event
> loop to handle the extended events as TARGET_WAITKIND_STOPPED.
>
> gdbserver's event loop could probably use a cleanup . For the time
> being, this looks good to me though.
>
>>     if (current_inferior->last_resume_kind == resume_stop
>>         && WSTOPSIG (w) == SIGSTOP)
>> @@ -3014,13 +3145,14 @@ retry:
>>         ourstatus->value.sig = GDB_SIGNAL_0;
>>       }
>>     else if (current_inferior->last_resume_kind == resume_stop
>> -       && WSTOPSIG (w) != SIGSTOP)
>> +       && WSTOPSIG (w) != SIGSTOP
>> +       && !extended_event_reported (ourstatus))
>>       {
>>         /* A thread that has been requested to stop by GDB with vCont;t,
>>        but, it stopped for other reasons.  */
>>         ourstatus->value.sig = gdb_signal_from_host (WSTOPSIG (w));
>>       }
>> -  else
>> +  else if (ourstatus->kind == TARGET_WAITKIND_STOPPED)
>>       {
>>         ourstatus->value.sig = gdb_signal_from_host (WSTOPSIG (w));
>>       }
>> @@ -3126,6 +3258,7 @@ static void
>>   send_sigstop (struct lwp_info *lwp)
>>   {
>>     int pid;
>> +  int ret;
>>
>>     pid = lwpid_of (get_lwp_thread (lwp));
>>
>> @@ -3143,7 +3276,21 @@ send_sigstop (struct lwp_info *lwp)
>>       debug_printf ("Sending sigstop to lwp %d\n", pid);
>>
>>     lwp->stop_expected = 1;
>> -  kill_lwp (pid, SIGSTOP);
>> +  errno = 0;
>> +  ret = kill_lwp (pid, SIGSTOP);
>> +  if (ret == -1 && errno == ESRCH)
>> +    {
>> +      /* If the kill fails with "No such process", on GNU/Linux we know
>> +     that the LWP has vanished - it is not a zombie, it is gone.
>> +     This is due to a thread other than the thread group leader
>> +     calling exec.  See comments in linux_low_filter_event regarding
>> +     PTRACE_EVENT_EXEC.  */
>> +      delete_lwp (lwp);
>> +      set_desired_inferior (0);
>> +
>> +      if (debug_threads)
>> +    debug_printf ("send_sigstop: lwp %d has vanished\n", pid);
>> +    }
>>   }
>>
>>   static int
>> diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h
>> index 7459710..7759f01 100644
>> --- a/gdb/gdbserver/linux-low.h
>> +++ b/gdb/gdbserver/linux-low.h
>> @@ -265,6 +265,11 @@ struct lwp_info
>>     /* When stopped is set, the last wait status recorded for this
>> lwp.  */
>>     int last_status;
>>
>> +  /* If WAITSTATUS->KIND != TARGET_WAITKIND_IGNORE, the waitstatus for
>> +     this LWP's last event.  This may correspond to LAST_STATUS above,
>> +     or to the current status during event processing.  */
>> +  struct target_waitstatus waitstatus;
>> +
>>     /* When stopped is set, this is where the lwp stopped, with
>>        decr_pc_after_break already accounted for.  */
>>     CORE_ADDR stop_pc;
>> diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c
>> index 4fcafa0..9ce25dc 100644
>> --- a/gdb/gdbserver/remote-utils.c
>> +++ b/gdb/gdbserver/remote-utils.c
>> @@ -1111,14 +1111,40 @@ prepare_resume_reply (char *buf, ptid_t ptid,
>>     switch (status->kind)
>>       {
>>       case TARGET_WAITKIND_STOPPED:
>> +    case TARGET_WAITKIND_EXECD:
>>         {
>>       struct thread_info *saved_inferior;
>>       const char **regp;
>>       struct regcache *regcache;
>> +    enum gdb_signal signal;
>> +
>> +    if (status->kind == TARGET_WAITKIND_EXECD)
>> +      signal = GDB_SIGNAL_TRAP;
>> +    else
>> +      signal = status->value.sig;
>> +
>> +    sprintf (buf, "T%02x", signal);
>>
>> -    sprintf (buf, "T%02x", status->value.sig);
>>       buf += strlen (buf);
>>
>> +    if (status->kind == TARGET_WAITKIND_EXECD && multi_process)
>> +      {
>> +        const char *event = "exec";
>> +        char hexified_pathname[PATH_MAX];
>> +
>> +        sprintf (buf, "%s:", event);
>> +        buf += strlen (buf);
>> +
>> +        /* Encode pathname to hexified format.  */
>> +        bin2hex ((const gdb_byte *) status->value.execd_pathname,
>> +             hexified_pathname, strlen(status->value.execd_pathname));
>> +
>> +        sprintf (buf, "%s;", hexified_pathname);
>> +        xfree (status->value.execd_pathname);
>> +        status->value.execd_pathname = NULL;
>> +        buf += strlen (buf);
>> +      }
>> +
>>       saved_inferior = current_inferior;
>>
>>       current_inferior = find_thread_ptid (ptid);
>> diff --git a/gdb/remote.c b/gdb/remote.c
>> index be8c423..f4412d8 100644
>> --- a/gdb/remote.c
>> +++ b/gdb/remote.c
>> @@ -5542,11 +5542,13 @@ remote_parse_stop_reply (char *buf, struct
>> stop_reply *event)
>>            pnum and set p1 to point to the character following it.
>>            Otherwise p1 points to p.  */
>>
>> -      /* If this packet is an awatch packet, don't parse the 'a'
>> -         as a register number.  */
>> +      /* If this packet has a stop reason string that starts
>> +         with a character that could be a hex digit, don't parse
>> +         it as a register number.  */
>>
>>         if (strncmp (p, "awatch", strlen("awatch")) != 0
>> -          && strncmp (p, "core", strlen ("core") != 0))
>> +          && strncmp (p, "core", strlen ("core") != 0)
>> +          && strncmp (p, "exec", strlen ("exec") != 0))
>>           {
>>             /* Read the ``P'' register number.  */
>>             pnum = strtol (p, &p_temp, 16);
>> @@ -5598,6 +5600,25 @@ Packet: '%s'\n"),
>>             p = unpack_varlen_hex (++p1, &c);
>>             event->core = c;
>>           }
>> +          else if (strncmp (p, "exec", p1 - p) == 0)
>> +        {
>> +          ULONGEST pid;
>> +          char pathname[PATH_MAX];
>> +
>> +          p = unpack_varlen_hex (++p1, &pid);
>> +
>> +          /* Save the pathname for event reporting and for
>> +             the next run command. */
>> +          hex2bin (p1, (gdb_byte *) pathname, (p - p1)/2);
>> +          /* Add the null terminator.  */
>> +          pathname[(p - p1)/2] = '\0';
>> +          /* This is freed during event handling.  */
>> +          event->ws.value.execd_pathname = xstrdup (pathname);
>> +          event->ws.kind = TARGET_WAITKIND_EXECD;
>> +          /* Save the pathname for the next run command.  */
>> +          xfree (remote_exec_file);
>> +          remote_exec_file = pathname;
>> +        }
>>             else
>>           {
>>             /* Silently skip unknown optional info.  */
>>
>
>
>
  
Tom Tromey May 21, 2014, 3:28 p.m. UTC | #4
>>>>> "Don" == Don Breazeal <donb@codesourcery.com> writes:

Don> One caveat: when an exec is detected, gdbserver emits a couple of warnings:
Don>     gdbserver: unexpected r_debug version 0
Don>     gdbserver: unexpected r_debug version 0
Don> However, debugging of shared libraries that are loaded by the exec'd
Don> program works just fine.  These messages may be caused by gdbserver making
Don> an attempt to initialize the solib hook before the r_debug structure has
Don> been initialized.  I intend to follow up in a subsequent patch.

I think it would be better to have that fix in the initial patch.
I am curious what others think though.

Don> diff --git a/gdb/common/linux-ptrace.c b/gdb/common/linux-ptrace.c
Don> index e3fc705..b137df9 100644
Don> --- a/gdb/common/linux-ptrace.c
Don> +++ b/gdb/common/linux-ptrace.c
Don> @@ -491,8 +491,11 @@ linux_test_for_tracefork (int child_pid)
Don>    if (ret == child_pid && WIFSTOPPED (status)
Don>        && status >> 16 == PTRACE_EVENT_EXIT)
Don>      {
Don> -        /* PTRACE_O_TRACEEXIT is supported.  */
Don> -        current_ptrace_options |= PTRACE_O_TRACEEXIT;
Don> +      /* PTRACE_O_TRACEEXIT is supported.  We use exit events to
Don> +	 implement support for exec events.  Since fork events are
Don> +	 supported we know exec events are supported, so we enable
Don> +	 exec events here.  */
Don> +      current_ptrace_options |= PTRACE_O_TRACEEXIT | PTRACE_O_TRACEEXEC;
Don>      }

There's code earlier in the function that is #ifdef GDBSERVER
and that checks PTRACE_O_TRACEEXEC for gdb.  It seems like more
unification there would be a better approach.

Don> +static int
Don> +extended_event_reported (const struct target_waitstatus *waitstatus)
Don> +{
Don> +
Don> +  if (waitstatus == NULL)
Don> +    return 0;
Don> +
Don> +  return waitstatus->kind == TARGET_WAITKIND_EXECD;

Extra newline after the "{".

I can't really comment on the rest, I'm afraid.

Tom
  

Patch

diff --git a/gdb/common/linux-ptrace.c b/gdb/common/linux-ptrace.c
index e3fc705..b137df9 100644
--- a/gdb/common/linux-ptrace.c
+++ b/gdb/common/linux-ptrace.c
@@ -491,8 +491,11 @@  linux_test_for_tracefork (int child_pid)
   if (ret == child_pid && WIFSTOPPED (status)
       && status >> 16 == PTRACE_EVENT_EXIT)
     {
-        /* PTRACE_O_TRACEEXIT is supported.  */
-        current_ptrace_options |= PTRACE_O_TRACEEXIT;
+      /* PTRACE_O_TRACEEXIT is supported.  We use exit events to
+	 implement support for exec events.  Since fork events are
+	 supported we know exec events are supported, so we enable
+	 exec events here.  */
+      current_ptrace_options |= PTRACE_O_TRACEEXIT | PTRACE_O_TRACEEXEC;
     }
 #endif
 }
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 90e7b15..5f94490 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -281,6 +281,32 @@  static int linux_event_pipe[2] = { -1, -1 };
 static void send_sigstop (struct lwp_info *lwp);
 static void wait_for_sigstop (void);
 
+/* Accepts an integer PID.  Returns a string containing the
+   name of the executable file for the child process.
+   Space for the result is malloc'd, caller must free.  */
+
+static char *
+linux_child_pid_to_exec_file (int pid)
+{
+  char *name1, *name2;
+
+  name1 = xmalloc (PATH_MAX);
+  name2 = xmalloc (PATH_MAX);
+  memset (name2, 0, PATH_MAX);
+
+  sprintf (name1, "/proc/%d/exe", pid);
+  if (readlink (name1, name2, PATH_MAX) > 0)
+    {
+      free (name1);
+      return name2;
+    }
+  else
+    {
+      free (name2);
+      return name1;
+    }
+}
+
 /* Return non-zero if HEADER is a 64-bit ELF file.  */
 
 static int
@@ -514,6 +540,19 @@  handle_extended_wait (struct lwp_info *event_child, int *wstatp)
               (PTRACE_TYPE_ARG4) 0);
       return ret;
     }
+  else if (event == PTRACE_EVENT_EXEC)
+    {
+      if (debug_threads)
+	debug_printf ("LHEW: Got exec event from LWP %ld\n",
+		      lwpid_of (event_thr));
+
+      event_child->waitstatus.kind = TARGET_WAITKIND_EXECD;
+      event_child->waitstatus.value.execd_pathname
+	= linux_child_pid_to_exec_file (lwpid_of (event_thr));
+
+      /* Report the event.  */
+      return 0;
+    }
   internal_error (__FILE__, __LINE__,
 	          _("unknown ptrace event %d"), event);
 }
@@ -1376,18 +1415,19 @@  check_zombie_leaders (void)
 	     program).  In the latter case, we can't waitpid the
 	     leader's exit status until all other threads are gone.
 
-	     - There are 3 or more threads in the group, and a thread
+	     - There are multiple threads in the group, and a thread
 	     other than the leader exec'd.  On an exec, the Linux
 	     kernel destroys all other threads (except the execing
 	     one) in the thread group, and resets the execing thread's
 	     tid to the tgid.  No exit notification is sent for the
 	     execing thread -- from the ptracer's perspective, it
 	     appears as though the execing thread just vanishes.
-	     Until we reap all other threads except the leader and the
-	     execing thread, the leader will be zombie, and the
-	     execing thread will be in `D (disc sleep)'.  As soon as
-	     all other threads are reaped, the execing thread changes
-	     it's tid to the tgid, and the previous (zombie) leader
+	     Until we reap all other threads (if any) except the
+	     leader and the execing thread, the leader will be zombie,
+	     and the execing thread will be in `D (disc sleep)'.  As
+	     soon as all other threads are reaped, or have reported
+	     PTRACE_EVENT_EXIT events, the execing thread changes its
+	     tid to the tgid, and the previous (zombie) leader
 	     vanishes, giving place to the "new" leader.  We could try
 	     distinguishing the exit and exec cases, by waiting once
 	     more, and seeing if something comes out, but it doesn't
@@ -1395,7 +1435,11 @@  check_zombie_leaders (void)
 	     we'll re-add the new one once we see the exec event
 	     (which is just the same as what would happen if the
 	     previous leader did exit voluntarily before some other
-	     thread execs).  */
+	     thread execs).
+
+	     Note that when PTRACE_EVENT_EXEC is supported, we use
+	     that mechanism to detect thread exit, including the
+	     exit of zombie leaders.  */
 
 	  if (debug_threads)
 	    fprintf (stderr,
@@ -1791,6 +1835,57 @@  linux_low_filter_event (ptid_t filter_ptid, int lwpid, int *wstatp)
 
   child = find_lwp_pid (pid_to_ptid (lwpid));
 
+  /* Check for stop events reported by a process we didn't already
+     know about - anything not already in our LWP list.
+
+     If we're expecting to receive stopped processes after
+     fork, vfork, and clone events, then we'll just add the
+     new one to our list and go back to waiting for the event
+     to be reported - the stopped process might be returned
+     from waitpid before or after the event is.
+
+     But note the case of a non-leader thread exec'ing after the
+     leader having exited, and gone from our lists.  On an exec,
+     the Linux kernel destroys all other threads (except the execing
+     one) in the thread group, and resets the execing thread's tid
+     to the tgid.  No exit notification is sent for the execing
+     thread -- from the ptracer's perspective, it appears as though
+     the execing thread just vanishes.  When they are available, we
+     use exit events (PTRACE_EVENT_EXIT) to detect thread exit
+     reliably.  As soon as all other threads (if any) are reaped or
+     have reported their PTRACE_EVENT_EXIT events, the execing
+     thread changes it's tid to the tgid, and the previous (zombie)
+     leader vanishes, giving place to the "new" leader.  The lwp
+     entry for the previous leader is deleted when we handle its
+     exit event, and we re-add the new one here.  */
+
+  if (WIFSTOPPED (wstat) && child == NULL
+      && (WSTOPSIG (wstat) == SIGTRAP && wstat >> 16 == PTRACE_EVENT_EXEC))
+    {
+      ptid_t child_ptid;
+
+      /* A multi-thread exec after we had seen the leader exiting.  */
+      if (debug_threads)
+	debug_printf ("LLW: Re-adding thread group leader LWP %d.\n",
+		      lwpid);
+
+      child_ptid = ptid_build (lwpid, lwpid, 0);
+      child = add_lwp (child_ptid);
+      child->stopped = 1;
+      current_inferior = child->thread;
+
+      if (non_stop && stopping_threads == NOT_STOPPING_THREADS)
+	{
+	  /* Make sure we delete the lwp entry for the exec'ing thread,
+	     which will have vanished.  We do this by sending a signal
+	     to all the other threads in the lwp list, deleting any
+	     that are not found.  Note that in all-stop mode this will
+	     happen before reporting the event.  */
+	  stop_all_lwps (0, child);
+	  unstop_all_lwps (0, child);
+	}
+    }
+
   /* If we didn't find a process, one of two things presumably happened:
      - A process we started and then detached from has exited.  Ignore it.
      - A process we are controlling has forked and the new child's stop
@@ -2122,8 +2217,7 @@  linux_wait_for_event_filtered (ptid_t wait_ptid, ptid_t filter_ptid,
 	 - When a non-leader thread execs, that thread just vanishes
 	   without reporting an exit (so we'd hang if we waited for it
 	   explicitly in that case).  The exec event is reported to
-	   the TGID pid (although we don't currently enable exec
-	   events).  */
+	   the TGID pid.  */
       errno = 0;
       ret = my_waitpid (-1, wstatp, options | WNOHANG);
 
@@ -2520,6 +2614,21 @@  linux_stabilize_threads (void)
     }
 }
 
+/* Return non-zero if WAITSTATUS reflects an extended linux
+   event.  Otherwise, return 0.  Note that extended EXIT
+   events are fixed up and handled like normal events, so
+   they are not considered here.  */
+
+static int
+extended_event_reported (const struct target_waitstatus *waitstatus)
+{
+
+  if (waitstatus == NULL)
+    return 0;
+
+  return waitstatus->kind == TARGET_WAITKIND_EXECD;
+}
+
 /* Wait for process, returns status.  */
 
 static ptid_t
@@ -2883,7 +2992,8 @@  retry:
 		       && !bp_explains_trap && !trace_event)
 		   || (gdb_breakpoint_here (event_child->stop_pc)
 		       && gdb_condition_true_at_breakpoint (event_child->stop_pc)
-		       && gdb_no_commands_at_breakpoint (event_child->stop_pc)));
+		       && gdb_no_commands_at_breakpoint (event_child->stop_pc))
+		   || extended_event_reported (&event_child->waitstatus));
 
   run_breakpoint_commands (event_child->stop_pc);
 
@@ -2905,6 +3015,15 @@  retry:
 			  paddress (event_child->stop_pc),
 			  paddress (event_child->step_range_start),
 			  paddress (event_child->step_range_end));
+	  if (debug_threads
+	      && extended_event_reported (&event_child->waitstatus))
+	    {
+	      char *str = target_waitstatus_to_string (ourstatus);
+	      debug_printf ("LWP %ld has forked, cloned, vforked or execd"
+			    " with waitstatus %s\n",
+			    lwpid_of (get_lwp_thread (event_child)), str);
+	      xfree (str);
+	    }
 	}
 
       /* We're not reporting this breakpoint to GDB, so apply the
@@ -3003,7 +3122,19 @@  retry:
 	unstop_all_lwps (1, event_child);
     }
 
-  ourstatus->kind = TARGET_WAITKIND_STOPPED;
+  if (extended_event_reported (&event_child->waitstatus))
+    {
+      /* If the reported event is a fork, vfork or exec, let GDB
+	 know.  */
+      ourstatus->kind = event_child->waitstatus.kind;
+      ourstatus->value = event_child->waitstatus.value;
+
+      /* Reset the event child's waitstatus since we handled it
+	 already.  */
+      event_child->waitstatus.kind = TARGET_WAITKIND_IGNORE;
+    }
+  else
+    ourstatus->kind = TARGET_WAITKIND_STOPPED;
 
   if (current_inferior->last_resume_kind == resume_stop
       && WSTOPSIG (w) == SIGSTOP)
@@ -3014,13 +3145,14 @@  retry:
       ourstatus->value.sig = GDB_SIGNAL_0;
     }
   else if (current_inferior->last_resume_kind == resume_stop
-	   && WSTOPSIG (w) != SIGSTOP)
+	   && WSTOPSIG (w) != SIGSTOP
+	   && !extended_event_reported (ourstatus))
     {
       /* A thread that has been requested to stop by GDB with vCont;t,
 	 but, it stopped for other reasons.  */
       ourstatus->value.sig = gdb_signal_from_host (WSTOPSIG (w));
     }
-  else
+  else if (ourstatus->kind == TARGET_WAITKIND_STOPPED)
     {
       ourstatus->value.sig = gdb_signal_from_host (WSTOPSIG (w));
     }
@@ -3126,6 +3258,7 @@  static void
 send_sigstop (struct lwp_info *lwp)
 {
   int pid;
+  int ret;
 
   pid = lwpid_of (get_lwp_thread (lwp));
 
@@ -3143,7 +3276,21 @@  send_sigstop (struct lwp_info *lwp)
     debug_printf ("Sending sigstop to lwp %d\n", pid);
 
   lwp->stop_expected = 1;
-  kill_lwp (pid, SIGSTOP);
+  errno = 0;
+  ret = kill_lwp (pid, SIGSTOP);
+  if (ret == -1 && errno == ESRCH)
+    {
+      /* If the kill fails with "No such process", on GNU/Linux we know
+	 that the LWP has vanished - it is not a zombie, it is gone.
+	 This is due to a thread other than the thread group leader
+	 calling exec.  See comments in linux_low_filter_event regarding
+	 PTRACE_EVENT_EXEC.  */
+      delete_lwp (lwp);
+      set_desired_inferior (0);
+
+      if (debug_threads)
+	debug_printf ("send_sigstop: lwp %d has vanished\n", pid);
+    }
 }
 
 static int
diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h
index 7459710..7759f01 100644
--- a/gdb/gdbserver/linux-low.h
+++ b/gdb/gdbserver/linux-low.h
@@ -265,6 +265,11 @@  struct lwp_info
   /* When stopped is set, the last wait status recorded for this lwp.  */
   int last_status;
 
+  /* If WAITSTATUS->KIND != TARGET_WAITKIND_IGNORE, the waitstatus for
+     this LWP's last event.  This may correspond to LAST_STATUS above,
+     or to the current status during event processing.  */
+  struct target_waitstatus waitstatus;
+
   /* When stopped is set, this is where the lwp stopped, with
      decr_pc_after_break already accounted for.  */
   CORE_ADDR stop_pc;
diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c
index 4fcafa0..9ce25dc 100644
--- a/gdb/gdbserver/remote-utils.c
+++ b/gdb/gdbserver/remote-utils.c
@@ -1111,14 +1111,40 @@  prepare_resume_reply (char *buf, ptid_t ptid,
   switch (status->kind)
     {
     case TARGET_WAITKIND_STOPPED:
+    case TARGET_WAITKIND_EXECD:
       {
 	struct thread_info *saved_inferior;
 	const char **regp;
 	struct regcache *regcache;
+	enum gdb_signal signal;
+
+	if (status->kind == TARGET_WAITKIND_EXECD)
+	  signal = GDB_SIGNAL_TRAP;
+	else
+	  signal = status->value.sig;
+
+	sprintf (buf, "T%02x", signal);
 
-	sprintf (buf, "T%02x", status->value.sig);
 	buf += strlen (buf);
 
+	if (status->kind == TARGET_WAITKIND_EXECD && multi_process)
+	  {
+	    const char *event = "exec";
+	    char hexified_pathname[PATH_MAX];
+
+	    sprintf (buf, "%s:", event);
+	    buf += strlen (buf);
+
+	    /* Encode pathname to hexified format.  */
+	    bin2hex ((const gdb_byte *) status->value.execd_pathname,
+		     hexified_pathname, strlen(status->value.execd_pathname));
+
+	    sprintf (buf, "%s;", hexified_pathname);
+	    xfree (status->value.execd_pathname);
+	    status->value.execd_pathname = NULL;
+	    buf += strlen (buf);
+	  }
+
 	saved_inferior = current_inferior;
 
 	current_inferior = find_thread_ptid (ptid);
diff --git a/gdb/remote.c b/gdb/remote.c
index be8c423..f4412d8 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -5542,11 +5542,13 @@  remote_parse_stop_reply (char *buf, struct stop_reply *event)
 	     pnum and set p1 to point to the character following it.
 	     Otherwise p1 points to p.  */
 
-	  /* If this packet is an awatch packet, don't parse the 'a'
-	     as a register number.  */
+	  /* If this packet has a stop reason string that starts
+	     with a character that could be a hex digit, don't parse
+	     it as a register number.  */
 
 	  if (strncmp (p, "awatch", strlen("awatch")) != 0
-	      && strncmp (p, "core", strlen ("core") != 0))
+	      && strncmp (p, "core", strlen ("core") != 0)
+	      && strncmp (p, "exec", strlen ("exec") != 0))
 	    {
 	      /* Read the ``P'' register number.  */
 	      pnum = strtol (p, &p_temp, 16);
@@ -5598,6 +5600,25 @@  Packet: '%s'\n"),
 		  p = unpack_varlen_hex (++p1, &c);
 		  event->core = c;
 		}
+	      else if (strncmp (p, "exec", p1 - p) == 0)
+		{
+		  ULONGEST pid;
+		  char pathname[PATH_MAX];
+
+		  p = unpack_varlen_hex (++p1, &pid);
+
+		  /* Save the pathname for event reporting and for
+		     the next run command. */
+		  hex2bin (p1, (gdb_byte *) pathname, (p - p1)/2);
+		  /* Add the null terminator.  */
+		  pathname[(p - p1)/2] = '\0';
+		  /* This is freed during event handling.  */
+		  event->ws.value.execd_pathname = xstrdup (pathname);
+		  event->ws.kind = TARGET_WAITKIND_EXECD;
+		  /* Save the pathname for the next run command.  */
+		  xfree (remote_exec_file);
+		  remote_exec_file = pathname;
+		}
 	      else
 		{
 		  /* Silently skip unknown optional info.  */