public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH v4 0/7] Remote fork events
       [not found] <201501252151.t0PLo3jE018308@mx0b-0013c101.pphosted.com>
@ 2015-01-26  4:43 ` Breazeal, Don
  2015-02-06 20:02   ` Breazeal, Don
  2015-02-27  0:46   ` [PATCH v5 0/6] " Don Breazeal
  0 siblings, 2 replies; 30+ messages in thread
From: Breazeal, Don @ 2015-01-26  4:43 UTC (permalink / raw)
  To: gdb-patches, Pedro Alves


From: Don Breazeal <donb@codesourcery.com>
Subject: [PATCH v4 0/7] Remote fork events
In-Reply-To: <54C236B9.8060200@redhat.com>


This is an update to the patch series implementing remote follow-fork.
This update only implements fork and vfork events, omitting exec events
for now.

The series has been renumbered, since the numbering of the original patch
series has little relevance now.  Let me know if I should maintain the
original numbering.

The primary difference between this series and previous versions is that
it includes support for 'target remote' as well as extended-remote.  It
also eliminates the vFollowFork packet used in previous versions and uses
the qSupported packet to determine if fork events are supported by
gdbserver.

The new series includes seven patches as follows:

1/7: Preparatory patch that implements qSupported support for fork events
     and associated mechanisms.

2/7: Implements functions to clone breakpoint lists in gdbserver.

3/7: Implements follow fork for 'fork' but not 'vfork' and for
extended-remote
     targets only.  Note subject line was garbled, see ERRATA below.

4/7: Extends follow fork for 'fork' to 'target remote' as well as
     extended-remote.

5/7: Adds the architecture-specific pieces of follow-fork that allows
     hardware watchpoints to be inherited by a forked child.

6/7: Adds follow fork for 'vfork'.

7/7: Adds catchpoints for 'fork' and 'vfork', along with support for
     killing a process that has forked but has not been followed.


ERRATA:
I have to apologize in advance for a few minor formatting and coding
errors, listed below.  I decided that these were trivial enough that
they didn't warrant the delay required to go through the whole rebase/test
cycle.  The issues are:

4/7: in linux-low.c there is a blank line containing spaces.
4/7: in remote.c, I left a /* XXX */ comment.
4/7: in remote.c there is an unnecessary static declaration of
     extended_remote_kill

The subject line of patch 3 was garbled somehow.  Sent on 25-Jan-2015,
it reads "Re: [PATCH v4 3/7 v3] Extended-remote Linux follow fork" when
it should read "[PATCH v4 3/7] Extended-remote-Linux follow fork". My
incompetent use of --in-reply-to with git send-email, I'm sure.


TESTING:
Testing was mostly done using x86_64 Ubuntu, with the exception of the
architecture-specific patch, #5.  There are a few anomalies that show
up, but don't signify any problem.

 - Intermediate patches show failures due to the lack of features
   implemented in subsequent patches, like missing hardware watchpoint
   or catchpoint support.

 - Some vfork tests fail due to the lack of exec event support.

 - There are a couple of tests that show new failures that actually
   fail in the current mainline.  Details of these are as follows:

   * when vfork events are enabled, gdb.base/disp-step-syscall.exp
     shows PASS => FAIL in .sum diffs.  The test actually always
     fails.  With native/master, we see

      stepi^M
      FAIL: gdb.base/disp-step-syscall.exp: vfork: stepi vfork insn
(timeout)

     With remote and extended-remote/master, we see a bogus PASS result:
      stepi^M
      [Inferior 1 (process 9399) exited normally]^M
      (gdb) PASS: gdb.base/disp-step-syscall.exp: vfork: stepi vfork insn

    The criteria to pass that test are pretty lax:
      gdb_test "stepi" ".*" "stepi $syscall insn"

  * Similarly, once vfork events are enabled, gdb.base/watch-vfork.exp
    shows PASS => FAIL in .sum diffs.  This test also always fails.  With
    native/master, we see this:

     continue^M
     Continuing.^M
     FAIL: gdb.base/watch-vfork.exp: Watchpoint triggers after vfork
(sw) (timeout)

    With extended-remote/master, we see this:
     continue
     Continuing.
     [Inferior 1 (process 18866) exited normally]
     (gdb) FAIL: gdb.base/watch-vfork.exp: Watchpoint triggers after
vfork (sw) (the program exited)

    But once vfork events are enabled, we see:
     continue
     Continuing.
     Detaching from process 17405
     FAIL: gdb.base/watch-vfork.exp: Watchpoint triggers after vfork
(sw) (timeout)
    which more-or-less matches the native/master behavior.

Thanks,
--Don



^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v4 0/7] Remote fork events
  2015-01-26  4:43 ` [PATCH v4 0/7] Remote fork events Breazeal, Don
@ 2015-02-06 20:02   ` Breazeal, Don
  2015-02-27  0:46   ` [PATCH v5 0/6] " Don Breazeal
  1 sibling, 0 replies; 30+ messages in thread
From: Breazeal, Don @ 2015-02-06 20:02 UTC (permalink / raw)
  To: gdb-patches, Pedro Alves

Ping.

Full remote follow fork patch series includes:
1/7: https://sourceware.org/ml/gdb-patches/2015-01/msg00686.html
2/7: https://sourceware.org/ml/gdb-patches/2015-01/msg00687.html
3/7: https://sourceware.org/ml/gdb-patches/2015-01/msg00689.html
4/7: https://sourceware.org/ml/gdb-patches/2015-01/msg00690.html
5/7: https://sourceware.org/ml/gdb-patches/2015-01/msg00685.html
6/7: https://sourceware.org/ml/gdb-patches/2015-01/msg00684.html
7/7: https://sourceware.org/ml/gdb-patches/2015-01/msg00688.html

Thanks,
--Don

On 1/25/2015 1:58 PM, Breazeal, Don wrote:
> 
> From: Don Breazeal <donb@codesourcery.com>
> Subject: [PATCH v4 0/7] Remote fork events
> In-Reply-To: <54C236B9.8060200@redhat.com>
> 
> 
> This is an update to the patch series implementing remote follow-fork.
> This update only implements fork and vfork events, omitting exec events
> for now.
> 
> The series has been renumbered, since the numbering of the original patch
> series has little relevance now.  Let me know if I should maintain the
> original numbering.
> 
> The primary difference between this series and previous versions is that
> it includes support for 'target remote' as well as extended-remote.  It
> also eliminates the vFollowFork packet used in previous versions and uses
> the qSupported packet to determine if fork events are supported by
> gdbserver.
> 
> The new series includes seven patches as follows:
> 
> 1/7: Preparatory patch that implements qSupported support for fork events
>      and associated mechanisms.
> 
> 2/7: Implements functions to clone breakpoint lists in gdbserver.
> 
> 3/7: Implements follow fork for 'fork' but not 'vfork' and for
> extended-remote
>      targets only.  Note subject line was garbled, see ERRATA below.
> 
> 4/7: Extends follow fork for 'fork' to 'target remote' as well as
>      extended-remote.
> 
> 5/7: Adds the architecture-specific pieces of follow-fork that allows
>      hardware watchpoints to be inherited by a forked child.
> 
> 6/7: Adds follow fork for 'vfork'.
> 
> 7/7: Adds catchpoints for 'fork' and 'vfork', along with support for
>      killing a process that has forked but has not been followed.
> 
> 
> ERRATA:
> I have to apologize in advance for a few minor formatting and coding
> errors, listed below.  I decided that these were trivial enough that
> they didn't warrant the delay required to go through the whole rebase/test
> cycle.  The issues are:
> 
> 4/7: in linux-low.c there is a blank line containing spaces.
> 4/7: in remote.c, I left a /* XXX */ comment.
> 4/7: in remote.c there is an unnecessary static declaration of
>      extended_remote_kill
> 
> The subject line of patch 3 was garbled somehow.  Sent on 25-Jan-2015,
> it reads "Re: [PATCH v4 3/7 v3] Extended-remote Linux follow fork" when
> it should read "[PATCH v4 3/7] Extended-remote-Linux follow fork". My
> incompetent use of --in-reply-to with git send-email, I'm sure.
> 
> 
> TESTING:
> Testing was mostly done using x86_64 Ubuntu, with the exception of the
> architecture-specific patch, #5.  There are a few anomalies that show
> up, but don't signify any problem.
> 
>  - Intermediate patches show failures due to the lack of features
>    implemented in subsequent patches, like missing hardware watchpoint
>    or catchpoint support.
> 
>  - Some vfork tests fail due to the lack of exec event support.
> 
>  - There are a couple of tests that show new failures that actually
>    fail in the current mainline.  Details of these are as follows:
> 
>    * when vfork events are enabled, gdb.base/disp-step-syscall.exp
>      shows PASS => FAIL in .sum diffs.  The test actually always
>      fails.  With native/master, we see
> 
>       stepi^M
>       FAIL: gdb.base/disp-step-syscall.exp: vfork: stepi vfork insn
> (timeout)
> 
>      With remote and extended-remote/master, we see a bogus PASS result:
>       stepi^M
>       [Inferior 1 (process 9399) exited normally]^M
>       (gdb) PASS: gdb.base/disp-step-syscall.exp: vfork: stepi vfork insn
> 
>     The criteria to pass that test are pretty lax:
>       gdb_test "stepi" ".*" "stepi $syscall insn"
> 
>   * Similarly, once vfork events are enabled, gdb.base/watch-vfork.exp
>     shows PASS => FAIL in .sum diffs.  This test also always fails.  With
>     native/master, we see this:
> 
>      continue^M
>      Continuing.^M
>      FAIL: gdb.base/watch-vfork.exp: Watchpoint triggers after vfork
> (sw) (timeout)
> 
>     With extended-remote/master, we see this:
>      continue
>      Continuing.
>      [Inferior 1 (process 18866) exited normally]
>      (gdb) FAIL: gdb.base/watch-vfork.exp: Watchpoint triggers after
> vfork (sw) (the program exited)
> 
>     But once vfork events are enabled, we see:
>      continue
>      Continuing.
>      Detaching from process 17405
>      FAIL: gdb.base/watch-vfork.exp: Watchpoint triggers after vfork
> (sw) (timeout)
>     which more-or-less matches the native/master behavior.
> 
> Thanks,
> --Don
> 
> 
> 

^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH v5 01/06] Identify remote fork event support
  2015-02-27  0:46   ` [PATCH v5 0/6] " Don Breazeal
@ 2015-02-27  0:46     ` Don Breazeal
  2015-02-27  0:47     ` [PATCH v5 04/06] Arch-specific remote follow fork Don Breazeal
                       ` (7 subsequent siblings)
  8 siblings, 0 replies; 30+ messages in thread
From: Don Breazeal @ 2015-02-27  0:46 UTC (permalink / raw)
  To: gdb-patches, palves

This is an updated version of the patch last reviewed here:
https://sourceware.org/ml/gdb-patches/2015-02/msg00260.html

Hi Pedro, here are responses to your review comments:

On 2/10/2015 8:34 AM, Pedro Alves wrote:
> On 01/25/2015 09:46 PM, Don Breazeal wrote:
...
>> There is an #if that has been added temporarily to allow the code for
>>...
> (Nit: I'd prefer that these code bits / hunks were simply moved
> to the patches that make use of them.)

Done.

>> +  linux_ptrace_set_additional_flags (0);
>
> Likewise, seems pointless add this call now.

Removed.

>> -	  current_ptrace_options |= (additional_flags & (PTRACE_O_TRACEFORK
>> -                                                        | PTRACE_O_TRACEVFORK
>> -                                                        | PTRACE_O_TRACEEXEC));
>> +	  current_ptrace_options |= (additional_flags
>> +				     & (PTRACE_O_TRACEFORK
>> +					| PTRACE_O_TRACEVFORK
>> +					| PTRACE_O_TRACEEXEC));
>
> This looks like a spurious change?

Yes.  Fixed.

>> -    linux_check_ptrace_features ();
>> +    linux_test_for_event_reporting ();
> 
> I don't really understand the motivation for the renaming.
> The function still tests all kinds of ptrace features, not just
> event-related features. E.g., it tests PTRACE_O_EXITKILL.  And the
> variable it controls is called "ptrace_options".  How about
> leaving the function's name as is?

It was an attempt to give functions managing the same mechanisms to
have a common naming convention.  But it is unnecessary and I have
restored the original name.

> The patch should also include a GDB manual change describing the
> new RSP features, and a NEWS change mentioning them.

Added.

An updated patch, including updated commit message, follow.  The only
substantive change to the commit message was to remove the paragraph
about the temporary #if statements, which don't appear in this
version of the patch.

Thanks,
--Don

This patch implements a mechanism for GDB to determine whether fork
events are supported in gdbserver.  This is a preparatory patch for
remote fork and exec event support.

Two new RSP packets are defined to represent fork and vfork event
support.  These packets are used just like PACKET_multiprocess_feature
to denote whether the corresponding event is supported.  GDB sends
fork-events+ and vfork-events+ to gdbserver to inquire about fork
event support.  If the response enables these packets, then GDB
knows that gdbserver supports the corresponding events and will
enable them.

Target functions used to query for support are included along with
each new packet.

In order for gdbserver to know whether the events are supported at the
point where the qSupported packet arrives, the code in nat/linux-ptrace.c
had to be reorganized.  Previously it would test for fork/exec event
support, then enable the events using the pid of the inferior.  When the
qSupported packet arrives there may not be an inferior.  So the mechanism
was split into two parts: a function that checks whether the events are
supported, called when gdbserver starts up, and another that enables the
events when the inferior stops for the first time.

Another gdbserver change was to add some global variables similar to
multi_process, one per new packet.  These are used to control whether
the corresponding fork events are enabled.  If GDB does not inquire
about the event support in the qSupported packet, then gdbserver will
not set these "report the event" flags.  If the flags are not set, the
events are ignored like they were in the past.  Thus, gdbserver will
never send fork event notification to an older GDB that doesn't
recognize fork events.

Tested on Ubuntu x64, native/remote/extended-remote, and as part of
subsequent patches in the series.

gdb/doc/
2015-02-25  Don Breazeal  <donb@codesourcery.com>

	* gdb.texinfo (General Query Packets): Add remote protocol
	features 'fork-events' and 'vfork-events'.

gdb/gdbserver/
2015-02-25  Don Breazeal  <donb@codesourcery.com>

	* linux-low.c (linux_supports_fork_events): New function.
	(linux_supports_vfork_events): New function.
	(linux_target_ops): Initialize new structure members.
	(initialize_low): Call linux_check_ptrace_features.
	* lynx-low.c (lynx_target_ops): Initialize new structure
	members.
	* server.c (report_fork_events, report_vfork_events):
	New global flags.
	(handle_query): Add new features to qSupported packet.
	(captured_main): Initialize new global variables.
	* target.h (struct target_ops) <supports_fork_events>:
	New member.
	<supports_vfork_events>: New member.
	(target_supports_fork_events): New macro.
	(target_supports_vfork_events): New macro.
	* win32-low.c (win32_target_ops): Initialize new structure
	members.

gdb/
2015-02-25  Don Breazeal  <donb@codesourcery.com>

	* NEWS: Announce new fork event feature support in
	qSupported packet.
	* nat/linux-ptrace.c (linux_check_ptrace_features): Change
	from static to extern.
	* nat/linux-ptrace.h (linux_check_ptrace_features): Declare.
	* remote.c (anonymous enum): <PACKET_fork_event_feature,
	* PACKET_vfork_event_feature>: New enumeration constants.
	(remote_query_supported): Add new feature queries to qSupported
	packet.
	(remote_protocol_features): Add table entries for new packets.
	(_initialize_remote): Exempt new packets from the requirement
	to have 'set remote' commands.

---
 gdb/NEWS                  |  3 +++
 gdb/doc/gdb.texinfo       | 12 ++++++++++++
 gdb/gdbserver/linux-low.c | 21 +++++++++++++++++++++
 gdb/gdbserver/lynx-low.c  |  2 ++
 gdb/gdbserver/server.c    | 22 ++++++++++++++++++++++
 gdb/gdbserver/target.h    | 14 ++++++++++++++
 gdb/gdbserver/win32-low.c |  2 ++
 gdb/nat/linux-ptrace.c    |  2 +-
 gdb/nat/linux-ptrace.h    |  1 +
 gdb/remote.c              | 23 +++++++++++++++++++++--
 10 files changed, 99 insertions(+), 3 deletions(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index b79b162..d7fe430 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -71,6 +71,9 @@ Qbtrace-conf:bts:size
 * GDB now has support for DTrace USDT (Userland Static Defined
   Tracing) probes.  The supported targets are x86_64-*-linux-gnu.
 
+* The remote stub now reports support for fork and vfork events to GDB's
+qSupported query.
+
 *** Changes in GDB 7.9
 
 * GDB now supports hardware watchpoints on x86 GNU Hurd.
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index e84a251..9834709 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -35762,6 +35762,18 @@ extensions unless the stub also reports that it supports them by
 including @samp{multiprocess+} in its @samp{qSupported} reply.
 @xref{multiprocess extensions}, for details.
 
+@item fork-events
+This feature indicates whether @value{GDBN} supports fork event 
+extensions to the remote protocol.  @value{GDBN} does not use such
+extensions unless the stub also reports that it supports them by
+including @samp{fork-events+} in its @samp{qSupported} reply.
+
+@item vfork-events
+This feature indicates whether @value{GDBN} supports vfork event 
+extensions to the remote protocol.  @value{GDBN} does not use such
+extensions unless the stub also reports that it supports them by
+including @samp{vfork-events+} in its @samp{qSupported} reply.
+
 @item xmlRegisters
 This feature indicates that @value{GDBN} supports the XML target
 description.  If the stub sees @samp{xmlRegisters=} with target
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 1c66985..5eb7576 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -5154,6 +5154,22 @@ linux_supports_multi_process (void)
   return 1;
 }
 
+/* Check if fork events are supported.  */
+
+static int
+linux_supports_fork_events (void)
+{
+  return linux_supports_tracefork ();
+}
+
+/* Check if vfork events are supported.  */
+
+static int
+linux_supports_vfork_events (void)
+{
+  return linux_supports_tracefork ();
+}
+
 static int
 linux_supports_disable_randomization (void)
 {
@@ -6114,6 +6130,8 @@ static struct target_ops linux_target_ops = {
   linux_async,
   linux_start_non_stop,
   linux_supports_multi_process,
+  linux_supports_fork_events,
+  linux_supports_vfork_events,
 #ifdef USE_THREAD_DB
   thread_db_handle_monitor_command,
 #else
@@ -6190,4 +6208,7 @@ initialize_low (void)
   sigaction (SIGCHLD, &sigchld_action, NULL);
 
   initialize_low_arch ();
+
+  /* Enable extended ptrace events.  */
+  linux_check_ptrace_features ();
 }
diff --git a/gdb/gdbserver/lynx-low.c b/gdb/gdbserver/lynx-low.c
index 98797ba..1126103 100644
--- a/gdb/gdbserver/lynx-low.c
+++ b/gdb/gdbserver/lynx-low.c
@@ -754,6 +754,8 @@ static struct target_ops lynx_target_ops = {
   NULL,  /* async */
   NULL,  /* start_non_stop */
   NULL,  /* supports_multi_process */
+  NULL,  /* supports_fork_events */
+  NULL,  /* supports_vfork_events */
   NULL,  /* handle_monitor_command */
 };
 
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index 156fcc8..1efcbc0 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -57,6 +57,8 @@ static int exit_requested;
 int run_once;
 
 int multi_process;
+int report_fork_events;
+int report_vfork_events;
 int non_stop;
 
 /* Whether we should attempt to disable the operating system's address
@@ -1977,6 +1979,18 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
 		  /* GDB supports relocate instruction requests.  */
 		  gdb_supports_qRelocInsn = 1;
 		}
+	      if (strcmp (p, "fork-events+") == 0)
+		{
+		  /* GDB supports and wants fork events if possible.  */
+		  if (target_supports_fork_events ())
+		    report_fork_events = 1;
+		}
+	      if (strcmp (p, "vfork-events+") == 0)
+		{
+		  /* GDB supports and wants vfork events if possible.  */
+		  if (target_supports_vfork_events ())
+		    report_vfork_events = 1;
+		}
 	      else
 		target_process_qsupported (p);
 
@@ -2027,6 +2041,12 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
       if (target_supports_multi_process ())
 	strcat (own_buf, ";multiprocess+");
 
+      if (target_supports_fork_events ())
+	strcat (own_buf, ";fork-events+");
+
+      if (target_supports_vfork_events ())
+	strcat (own_buf, ";vfork-events+");
+
       if (target_supports_non_stop ())
 	strcat (own_buf, ";QNonStop+");
 
@@ -3373,6 +3393,8 @@ captured_main (int argc, char *argv[])
 
       noack_mode = 0;
       multi_process = 0;
+      report_fork_events = 0;
+      report_vfork_events = 0;
       /* Be sure we're out of tfind mode.  */
       current_traceframe = -1;
       cont_thread = null_ptid;
diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
index 05feb36..74ed07c 100644
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -262,6 +262,12 @@ struct target_ops
   /* Returns true if the target supports multi-process debugging.  */
   int (*supports_multi_process) (void);
 
+  /* Returns true if fork events are supported.  */
+  int (*supports_fork_events) (void);
+
+  /* Returns true if vfork events are supported.  */
+  int (*supports_vfork_events) (void);
+
   /* If not NULL, target-specific routine to process monitor command.
      Returns 1 if handled, or 0 to perform default processing.  */
   int (*handle_monitor_command) (char *);
@@ -393,6 +399,14 @@ void set_target_ops (struct target_ops *);
 
 int kill_inferior (int);
 
+#define target_supports_fork_events() \
+  (the_target->supports_fork_events ? \
+   (*the_target->supports_fork_events) () : 0)
+
+#define target_supports_vfork_events() \
+  (the_target->supports_vfork_events ? \
+   (*the_target->supports_vfork_events) () : 0)
+
 #define detach_inferior(pid) \
   (*the_target->detach) (pid)
 
diff --git a/gdb/gdbserver/win32-low.c b/gdb/gdbserver/win32-low.c
index e3fb618..210a747 100644
--- a/gdb/gdbserver/win32-low.c
+++ b/gdb/gdbserver/win32-low.c
@@ -1823,6 +1823,8 @@ static struct target_ops win32_target_ops = {
   NULL, /* async */
   NULL, /* start_non_stop */
   NULL, /* supports_multi_process */
+  NULL, /* supports_fork_events */
+  NULL, /* supports_vfork_events */
   NULL, /* handle_monitor_command */
   NULL, /* core_of_thread */
   NULL, /* read_loadmap */
diff --git a/gdb/nat/linux-ptrace.c b/gdb/nat/linux-ptrace.c
index 1c67819..ba934af 100644
--- a/gdb/nat/linux-ptrace.c
+++ b/gdb/nat/linux-ptrace.c
@@ -337,7 +337,7 @@ static void linux_test_for_exitkill (int child_pid);
 
 /* Determine ptrace features available on this target.  */
 
-static void
+void
 linux_check_ptrace_features (void)
 {
   int child_pid, ret, status;
diff --git a/gdb/nat/linux-ptrace.h b/gdb/nat/linux-ptrace.h
index c5b0f14..c7bcfb0 100644
--- a/gdb/nat/linux-ptrace.h
+++ b/gdb/nat/linux-ptrace.h
@@ -98,6 +98,7 @@ extern void linux_ptrace_attach_fail_reason (pid_t pid, struct buffer *buffer);
 extern char *linux_ptrace_attach_fail_reason_string (ptid_t ptid, int err);
 
 extern void linux_ptrace_init_warnings (void);
+extern void linux_check_ptrace_features (void);
 extern void linux_enable_event_reporting (pid_t pid, int attached);
 extern void linux_disable_event_reporting (pid_t pid);
 extern int linux_supports_tracefork (void);
diff --git a/gdb/remote.c b/gdb/remote.c
index 3479140..50b6d45 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -1338,6 +1338,12 @@ enum {
   /* Support for the Qbtrace-conf:bts:size packet.  */
   PACKET_Qbtrace_conf_bts_size,
 
+  /* Support for fork events.  */
+  PACKET_fork_event_feature,
+
+  /* Support for vfork events.  */
+  PACKET_vfork_event_feature,
+
   PACKET_MAX
 };
 
@@ -4025,7 +4031,11 @@ static const struct protocol_feature remote_protocol_features[] = {
   { "qXfer:btrace-conf:read", PACKET_DISABLE, remote_supported_packet,
     PACKET_qXfer_btrace_conf },
   { "Qbtrace-conf:bts:size", PACKET_DISABLE, remote_supported_packet,
-    PACKET_Qbtrace_conf_bts_size }
+    PACKET_Qbtrace_conf_bts_size },
+  { "fork-events", PACKET_DISABLE, remote_supported_packet,
+    PACKET_fork_event_feature },
+  { "vfork-events", PACKET_DISABLE, remote_supported_packet,
+    PACKET_vfork_event_feature }
 };
 
 static char *remote_support_xml;
@@ -4099,6 +4109,12 @@ remote_query_supported (void)
 
       q = remote_query_supported_append (q, "qRelocInsn+");
 
+      if (rs->extended)
+	{
+	  q = remote_query_supported_append (q, "fork-events+");
+	  q = remote_query_supported_append (q, "vfork-events+");
+	}
+
       q = reconcat (q, "qSupported:", q, (char *) NULL);
       putpkt (q);
 
@@ -12319,7 +12335,8 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
   add_packet_config_cmd (&remote_protocol_packets[PACKET_Qbtrace_conf_bts_size],
        "Qbtrace-conf:bts:size", "btrace-conf-bts-size", 0);
 
-  /* Assert that we've registered commands for all packet configs.  */
+  /* Assert that we've registered "set remote foo-packet" commands
+     for all packet configs.  */
   {
     int i;
 
@@ -12338,6 +12355,8 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
 	  case PACKET_DisconnectedTracing_feature:
 	  case PACKET_augmented_libraries_svr4_read_feature:
 	  case PACKET_qCRC:
+	  case PACKET_fork_event_feature:
+	  case PACKET_vfork_event_feature:
 	    /* Additions to this list need to be well justified:
 	       pre-existing packets are OK; new packets are not.  */
 	    excepted = 1;
-- 
1.8.1.1

^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH v5 0/6] Remote fork events
  2015-01-26  4:43 ` [PATCH v4 0/7] Remote fork events Breazeal, Don
  2015-02-06 20:02   ` Breazeal, Don
@ 2015-02-27  0:46   ` Don Breazeal
  2015-02-27  0:46     ` [PATCH v5 01/06] Identify remote fork event support Don Breazeal
                       ` (8 more replies)
  1 sibling, 9 replies; 30+ messages in thread
From: Don Breazeal @ 2015-02-27  0:46 UTC (permalink / raw)
  To: gdb-patches, palves

This is an update to the patch series implementing remote follow-fork.
This update only implements fork and vfork events for extended remote,
omitting exec events and 'target remote' for now.

Changes from the previous version include:
 * Removed 'target remote' support, which is deferred for later.
 * Fixes per review comments, notably
   - issues with unpushing the target after detach or kill
   - reorganizing/renaming some things, both to match the
     native implementation better and for just plain better
     organization.
   - fixed and/or improved some of the event handling code.
 * Also added documentation to each individual patch, rather than
   saving it all for a subsequent 'documentation patch'.

The new series includes six patches as follows:

1/6: Preparatory patch that implements qSupported support for fork events
     and associated mechanisms.

2/6: Implements functions to clone breakpoint lists in gdbserver.

3/6: Implements follow fork for 'fork' but not 'vfork', for
     extended-remote targets only.

4/6: Adds the architecture-specific pieces of follow-fork that allows
     hardware watchpoints to be inherited by a forked child.
     This patch was previously approved, although I have added a
     trivial change to gdb/NEWS in this patch.

5/6: Adds follow fork for 'vfork'.

6/6: Adds catchpoints for 'fork' and 'vfork', along with support for
     killing a process that has forked but has not been followed.

TESTING:
Testing was mostly done using x86_64 Ubuntu, with the exception of the
architecture-specific patch, #4.  There are a few anomalies that show
up, but don't signify any problem.

 - Intermediate patches show failures due to the lack of features
   implemented in subsequent patches, like missing hardware watchpoint
   or catchpoint support.

 - Some vfork tests fail due to the lack of exec event support.

 - There are a couple of tests that show new failures that actually
   fail in the current mainline.  Details of these are as follows:

   * when vfork events are enabled, gdb.base/disp-step-syscall.exp
     shows PASS => FAIL in .sum diffs.  The test actually always
     fails.  With native/master, we see

      stepi^M
      FAIL: gdb.base/disp-step-syscall.exp: vfork: stepi vfork insn
(timeout)

     With remote and extended-remote/master, we see a bogus PASS result:
      stepi^M
      [Inferior 1 (process 9399) exited normally]^M
      (gdb) PASS: gdb.base/disp-step-syscall.exp: vfork: stepi vfork insn

    The criteria to pass that test are pretty lax:
      gdb_test "stepi" ".*" "stepi $syscall insn"

  * Similarly, once vfork events are enabled, gdb.base/watch-vfork.exp
    shows PASS => FAIL in .sum diffs.  This test also always fails.  With
    native/master, we see this:

     continue^M
     Continuing.^M
     FAIL: gdb.base/watch-vfork.exp: Watchpoint triggers after vfork
(sw) (timeout)

    With extended-remote/master, we see this:
     continue
     Continuing.
     [Inferior 1 (process 18866) exited normally]
     (gdb) FAIL: gdb.base/watch-vfork.exp: Watchpoint triggers after
vfork (sw) (the program exited)

    But once vfork events are enabled, we see:
     continue
     Continuing.
     Detaching from process 17405
     FAIL: gdb.base/watch-vfork.exp: Watchpoint triggers after vfork
(sw) (timeout)
    which more-or-less matches the native/master behavior.

Thanks,
--Don

^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH v5 03/06] Extended-remote Linux follow fork
  2015-02-27  0:46   ` [PATCH v5 0/6] " Don Breazeal
                       ` (2 preceding siblings ...)
  2015-02-27  0:47     ` [PATCH v5 02/06] Clone remote breakpoints Don Breazeal
@ 2015-02-27  0:47     ` Don Breazeal
  2015-02-27  0:47     ` [PATCH v5 05/06] Remote follow vfork Don Breazeal
                       ` (4 subsequent siblings)
  8 siblings, 0 replies; 30+ messages in thread
From: Don Breazeal @ 2015-02-27  0:47 UTC (permalink / raw)
  To: gdb-patches, palves

This is an updated version of the patch last reviewed here:
https://sourceware.org/ml/gdb-patches/2015-02/msg00261.html

Hi Pedro,
Here are responses to your review comments:

On 2/10/2015 8:36 AM, Pedro Alves wrote:
> On 01/25/2015 09:46 PM, Don Breazeal wrote:
...
>>> I'm not seeing what would be linux-specific?  On remote_follow_fork
>>> fork, we switch the current remote thread to gdb's current
>>> thread (either parent or child), by
>>> calling 'set_general_thread (inferior_ptid);'
>>> And then if we need to detach parent or child, we detach it with
>>> the D;PID packet.
>> 
>> There are two linux-specific issues, both of which are workarounds for
>> kernel issues.
>> 
>> 1) workaround for the hardware single-step kernel bug that causes the 
>>    hardware single-step flag to be inherited across a vfork.  In
>>    linux-nat.c:linux_child_follow_fork we manually step the inferior
>>    before detaching.
>> 
>> 2) workaround for kernels that support PTRACE_O_TRACEVFORK but not
>>    PTRACE_O_TRACEVFORKDONE.  In linux-nat.c:linux_child_follow_fork
>>    we run the vfork child for a little while, then create a fake
>>    vfork-done event.
>> 
>> I propose that we don't do either of these workarounds for remote 
>> follow fork.  For (1), the kernel fix that eliminates the need for
>> this looks like it is from 2009.  For (2), the workaround is for
>> kernels between versions 2.5.46 (04.Nov.2002) and 2.6.18 (20.Sep.2006).
>> Is it necessary to provide workarounds in a new feature for kernels
>> as old as these?  (1) in particular is difficult to implement, since
>> gdbserver has no way to know whether detach-on-fork has been set, or
>> whether a given detach has anything to do with a vfork that preceded
>> it.  OK to drop these?
>
> OK, let's do that, even if just to focus on the essence first.

Thanks.

>>    - handle_extended_wait is given a return value, denoting whether the
>>      handled event should be reported to GDB.  Previously it had only
>>      handled clone events, which were never reported.
>
> This may need to be adjusted a bit when rebased on mainline.
> handle_extended_wait now never resumes any LWP.

I think there were some mainline changes in this area that I hadn't
caught.  I've made some changes here that I hope will address this concern.
Primarily linux_low_filter_event doesn't return after the call to
handle_extended_wait in the case where we get an event that we want to
report to GDB.

> We never, ever, send "T05 fork" to a GDB that does not understand that, right?
> Because although GDB skips magic register "numbers", "fork" starts with
> a hex digit and a GDB that doesn't understand it would be very confused.
> We did that mistake with "core", and broke backwards compatibility then.

The only time we will send GDB a "T05 fork" is when it has previously 
included "fork-events+" in the qSupported packet.  I did change the
"stop reason" from "fork" to "xfork" anyway, just to eliminate any
possible hex digit confusion.  I see that you changed how this is
handled, so maybe that change is now unnecessary.


>> diff --git a/gdb/gdbserver/gdbthread.h b/gdb/gdbserver/gdbthread.h
>> index b3e3e9d..33643af 100644
>> --- a/gdb/gdbserver/gdbthread.h
>> +++ b/gdb/gdbserver/gdbthread.h
>> @@ -41,6 +41,10 @@ struct thread_info
>>    /* True if LAST_STATUS hasn't been reported to GDB yet.  */
>>    int status_pending_p;
>>  
>> +  /* This is used to store fork and exec event information until
>> +     it is reported to GDB.  */
>> +  struct target_waitstatus pending_follow;
>> +
>
> I'm not sure this interact correctly with status_pending_p.  On the
> GDB/linux-nat.c side, the equivalent would be lp->waitstatus, not
> thread->pending_follow, and the former is considered a pending status.
> See linux-nat.c:lwp_status_pending_p.
> 
> Consider the case of debugging more than one process simulatenously,
> and two forking at the same time.  GDB is only going to report one
> fork event at a time.  So the other event will need to stay pending.
> 
> But I don't see anything that would make status_pending_p_callback
> understand that the forked LWP has a pending event to report, but
> that it has already been processed by handle_extended_wait.
> We can't process the same extended wait twice.  GDBserver would
> get confused and likely hang (waiting for the wait status of the child).

There are a couple of issues here, I think.
1) putting this field in the thread_info structure and naming it
   pending_follow wasn't consistent with the native side's implementation,
   where thread_info.pending_follow is used for a different purpose.  To
   be consistent with the native side, this should be in the lwp_info
   structure, named waitstatus.

I have made this change.

2) This field should interact with status_pending_p.

In the previous version of the patch, I had cleverly hidden the
assignment of status_pending_p in linux_low_filter_event after the
return from handle_extended_wait. :-S

In this new version I've put the assignment right next to the assignment of
lp->waitstatus inside handle_extended_wait.  Thanks for pointing this out;
it makes more sense this way.


>> +static void
>> +linux_low_enable_events (pid_t pid, int attached)
>> +{
>> +  if (!report_fork_events)
>> +    linux_ptrace_clear_additional_flags (PTRACE_O_TRACEFORK);
>
> I'm not sure about this bit.  We should also consider cases like:
> 
>   1. A GDB that supports extended-remote and fork events starts
>     a program.
>   2. GDB disconnects
>   3. A new GDB connection comes along.  But this time, this GDB
>     does not understand fork events.
> 
> And the reverse too.
> 
> Also, if a non-stop or disconnected tracing connection
> disconnects, it'll leave the program running.  When a new
> GDB connects, we may need to change the ptrace options
> to enable/disable these events.

In this version of the patch I've implemented a new gdbserver
target function that is called when a qSupported packet is
processed.  The assumption is that receipt of a qSupported
packet is equivalent to the start of a new GDB connection.

I've made some changes to linux-ptrace.c that keep track
of the "additional flags" that the OS supports, as well as
the set of valid "additional flags".  Another new function
uses this information to reset the ptrace options based on
what GDB requests in the qSupported option.

> 
> Alternatively, always enable the events at the ptrace
> level, and then filter them out if the connected
> GDB doesn't support them.

I made an attempt at doing this back when I switched to using the
qSupported packet for the fork event features, and concluded that
the complexity of handling the events but not reporting them wasn't
worth the benefit.  By not enabling them if GDB doesn't want them
we can just use the existing (working) code.

> 
> Or even, report a T05 / SIGTRAP to GDBs that don't support
> the events.  That may actually be better than not reporting
> anything like today, because what we do today (ignore the
> fork child) is just a recipe for it crashing when it trips
> on a breakpoint that was set on the parent.  A random SIGTRAP
> at least gives the user a chance of doing something.

My approach has been to make the implementation behave like
the native implementation and to keep the default case the
same as how things work today.  So although this sounds like
it would be a nice feature, I propose that we leave reporting
a SIGTRAP in this case for another day.  This patch series
has taken a while, and I'm concerned about the impact of
adding new functionality.

> 
> Hmm.  Maybe we should rename "T05 fork" to something that
> doesn't start with "f" (and hex char), and then that'd be
> the simplest.  We'd always send the event over.  That may
> be best, as it gives us more leverage even if we decide to
> not report the events to old GDB, and then change our minds.

As noted previously, this is now "T05 xfork'.

> 
> (Even with that, I think it'd be good to keep GDB / GDBserver
> broadcasting the support in qSupported.)

Agreed.

>> +/* Return non-zero if WAITSTATUS reflects an extended linux
>> +   event that gdbserver supports.  Otherwise, return zero.  */
>
> I don't understand "gdbserver supports" here.  This is gdbserver
> code, so anything gdbserver doesn't support shouldn't even
> compile, right?  :-)  Can you clarify this comment please?

You are right, that didn't make sense.  Removed "gdbserver supports".
Sorry for the noise.

>> +++ b/gdb/gdbserver/remote-utils.c
>> @@ -1114,12 +1114,24 @@ prepare_resume_reply (char *buf, ptid_t ptid,
>>    switch (status->kind)
>>      {
>>      case TARGET_WAITKIND_STOPPED:
>> +    case TARGET_WAITKIND_FORKED:
>>        {
>>  	struct thread_info *saved_thread;
>>  	const char **regp;
>>  	struct regcache *regcache;
>>  
>> -	sprintf (buf, "T%02x", status->value.sig);
>> +	if (status->kind == TARGET_WAITKIND_FORKED && multi_process)
>
> I'm not sure this "multi_process" check here is correct.
> Shouldn't it be "report_fork_events" instead?

You are correct.  Fixed.

>> +	    sprintf (buf, "T%02x%s:p%x.%lx;", signal, event,
>> +		     ptid_get_pid (status->value.related_pid),
>> +		     ptid_get_lwp (status->value.related_pid));
>
> Can this use write_ptid?

Yes, done.

>> +#include "inf-child.h"
>
> That's a layering violation.  inf-child.h is the
> base/prototype target for default child (native) targets.

Got it.  Fixed.

>> +static int
>> +remote_follow_fork (struct target_ops *target, int follow_child,
>> +		    int detach_fork)
>> +{
>> +  struct remote_state *rs = get_remote_state ();
>> +
>> +  /* Checking the fork event is sufficient for both fork and vfork.  */
>
> Why?

Because the fork and vfork events were both introduced in the same version
of the Linux kernel (as I expect you knew already :-) ).  But, for this
patch we only need fork events anyway, so I just removed the comment from
this patch.  I put an appropriately explanatory comment in the follow vfork
patch.  I could go ahead and add remote_vfork_event_p in the vfork patch 
instead, if that would be preferable.

>> +  if (remote_fork_event_p (rs))
>> +    {
>> +      if (detach_fork && !follow_child)
>> +	{
>> +	  ptid_t child_ptid;
>> +	  pid_t child_pid;
>> +
>> +	  gdb_assert ((inferior_thread ()->pending_follow.kind
>> +		       == TARGET_WAITKIND_FORKED));
>> +	  child_ptid = inferior_thread ()->pending_follow.value.related_pid;
>> +	  child_pid = ptid_get_pid (child_ptid);
>> +
>> +	  /* Tell the remote target to detach.  */
>> +	  xsnprintf (rs->buf, get_remote_packet_size (), "D;%x", child_pid);
>> +
>> +	  putpkt (rs->buf);
>> +	  getpkt (&rs->buf, &rs->buf_size, 0);
>> +
>> +	  if (rs->buf[0] == 'O' && rs->buf[1] == 'K')
>> +	    ;
>> +	  else if (rs->buf[0] == '\0')
>> +	    error (_("Remote doesn't know how to detach"));
>> +	  else
>> +	    error (_("Can't detach process."));
>
> Instead of copying/inlining this part of remote_detach_1 here,
> can you please factor out this bit to a function that is used
> both here and in remote_detach_1 ?

I figured out that I could just call remote_detach_1 from
remote_follow_fork.  I just had to set inferior_ptid to the
child's ptid, call remote_detach_1, then restore inferior_ptid
with the parent's ptid.

>> +
>> +	  inferior_ptid = null_ptid;
>> +	  detach_inferior (child_pid);
>> +	  inf_child_maybe_unpush_target (target);
>
> Please just do:
> 
>   if (!have_inferiors ())
>     unpush_target (target);
> 
> inf_child_maybe_unpush_target checks the inf_child_explicitly_opened
> flag that is completely unrelated to "target remote".  That's for
> "target native".
> 
> But wait, why would you ever want to unpush the target here?

You are right, we wouldn't want to.  Even for 'target remote', there
will still be at least one inferior at this point (the parent), so
we will always want to keep the target.

>> +remote_detach_1 (struct target_ops *ops, const char *args,
>> +		 int from_tty, int extended)
>>  {
>>    int pid = ptid_get_pid (inferior_ptid);
>>    struct remote_state *rs = get_remote_state ();
>> +  struct thread_info *tp = first_thread_of_process (pid);
>>  
>>    if (args)
>>      error (_("Argument given to \"detach\" when remotely debugging."));
>> @@ -4442,19 +4492,28 @@ remote_detach_1 (const char *args, int from_tty, int extended)
>>    if (from_tty && !extended)
>>      puts_filtered (_("Ending remote debugging.\n"));
>>  
>> -  target_mourn_inferior ();
>> +  /* If doing detach-on-fork, we don't mourn, because that will delete
>> +     breakpoints that should be available for the child.  */
>> +  if (tp->pending_follow.kind != TARGET_WAITKIND_FORKED)
>> +    target_mourn_inferior ();
>> +  else
>> +    {
>> +      inferior_ptid = null_ptid;
>> +      detach_inferior (pid);
>> +      inf_child_maybe_unpush_target (ops);
>
> Likewise.  If this is the last process, and we're doing
> extended-remote, this will disconnect from the target, but
> extended-remote shouldn't disconnect.  This doesn't look like
> will do the right thing?

It seemed to work OK, I assume because to get to the call to
inf_child_maybe_unpush_target it had to have been dealing with
a fork.  But you are right, the call is unnecessary, aside from
being the wrong function to call.

I think this was an artifact of my work on 'target remote'.  I expect
that we may revisit this sort of thing when we get to that patch,
although thanks to this review I understand it all much better.

The updated version looks like this:

+  /* If doing detach-on-fork, we don't mourn, because that will delete
+     breakpoints that should be available for the followed inferior.  */
+  if (!is_fork_child && !is_fork_parent)
+    target_mourn_inferior ();
+  else
+    {
+      inferior_ptid = null_ptid;
+      detach_inferior (pid);
+    }


>> diff --git a/gdb/testsuite/gdb.base/multi-forks.exp b/gdb/testsuite/gdb.base/multi-forks.exp
>> index e95cb4b..5682730 100644
>> --- a/gdb/testsuite/gdb.base/multi-forks.exp
>> +++ b/gdb/testsuite/gdb.base/multi-forks.exp
>> @@ -62,6 +62,18 @@ proc continue_to_exit_bp_loc {} {
>>      set seen_break 0
>>      set seen_prompt 0
>>      set seen_timeout 0
>> +
>> +    # If we are running with gdbserver, the output ($decimal done) will
>> +    # come via the spawn_id of gdbserver, not the spawn_id of gdb (the
>> +    # default).  So we grab the spawn_id of gdbserver, if it exists, and
>> +    # add it to the gdb_expect statement below using "-i", allowing us
>> +    # to apply the expect statement to the output of both spawn_ids.
>> +    global server_spawn_id
>> +    set current_spawn_id [board_info host fileid]
>> +    if {![info exists server_spawn_id]} {
>> +	set server_spawn_id ""
>> +    }
>> +
>
> Clever.  :-)  But it also means that the test will fail on
> true remote testing, right?
> 
> ISTM that if we don't have a server_spawn_id, the test should
> not be expecting the "done"s when gdb,noinferiorio is set.

I have changed the test so that if gdb,noinferiorio is set it
just ignores the test for the "done"s from the inferior.

I saw that you had implemented a more general version of the
spawn id changes.  I don't know if this will need to change
to work with that, but if it does presumably the changes will
be minor.

> +global server_spawn_id
>
> This should have a describing comment.
>
Done, but probably moot given your work on this.

>> +++ b/gdb/testsuite/lib/gdbserver-support.exp
>> @@ -39,7 +39,6 @@
>>  #	After GDB starts you should check global $gdbserver_gdbport for the
>>  #	real port used.  It is not useful if $gdbserver_reconnect_p was not set.
>>  #
>> -
>>  #
>
> Please don't remove that line.  It was splitting different sections.
> Above is baseboard config options.  Below is description of the
> following procedure.

Sorry about that.  I'll have to add "removed blank line" to the list of
common formatting errors I check for.

The updated patch is below.
Thanks!
--Don


This patch implements basic support for follow-fork and detach-on-fork on
extended-remote Linux targets.  Only 'fork' is supported in this patch;
'vfork' support is added n a subsequent patch.  This patch depends on 
the previous patches in the patch series.

Sufficient extended-remote functionality has been implemented here to pass
gdb.base/multi-forks.exp, as well as gdb.base/foll-fork.exp with the
catchpoint tests commented out.  Some other fork tests fail with this
patch because it doesn't provide the architecture support needed for
watchpoint inheritance or fork catchpoints.

The implementation follows the same general structure as for the native
implementation as much as possible.

This implementation included:
 * enabling fork events in linux-low.c in initialize_low and
   linux_enable_extended_features

 * handling fork events in gdbserver/linux-low.c:handle_extended_wait

   - when a fork event occurs in gdbserver, we must do the full creation
     of the new process, thread, lwp, and breakpoint lists.  This is
     required whether or not the new child is destined to be
     detached-on-fork, because GDB will make target calls that require all
     the structures.  In particular we need the breakpoint lists in order
     to remove the breakpoints from a detaching child.  If we are not
     detaching the child we will need all these structures anyway.

   - as part of this event handling we store the target_waitstatus in a new
     member of the parent thread_info structure, 'pending_follow'.  This
     is used to store extended event information for reporting to GDB.

   - handle_extended_wait is given a return value, denoting whether the
     handled event should be reported to GDB.  Previously it had only
     handled clone events, which were never reported.

 * using a new predicate in gdbserver to control handling of the fork event
   (and eventually all extended events) in linux_wait_1.  The predicate,
   extended_event_reported, checks a target_waitstatus.kind for an
   extended ptrace event.

 * implementing a new RSP 'T' Stop Reply Packet stop reason: "fork", in
   gdbserver/remote-utils.c and remote.c.

 * implementing new target and RSP support for target_follow_fork with
   target extended-remote.  (The RSP components were actually defined in
   patch 4, but they see their first use here).

   - remote target routine remote_follow_fork, which just sends the 'D;pid'
     detach packet to detach the new fork child cleanly.  We can't just
     call target_detach because the data structures for the forked child
     have not been allocated on the host side.

Tested on x64 Ubuntu Lucid, native, remote, extended-remote.

gdb/doc/
2015-02-25  Don Breazeal  <donb@codesourcery.com>

	* gdb.texinfo (Forks): List targets that have fork debugging
	support.
	(Stop Reply Packets): List new stop reason "xfork".

gdb/gdbserver/
2015-02-25  Don Breazeal  <donb@codesourcery.com>

	* linux-low.c (handle_extended_wait): Implement return value,
	handle PTRACE_EVENT_FORK.
	(linux_low_enable_events): New function.
	(linux_low_filter_event): Use return value from
	handle_extended_wait.
	(extended_event_reported): New function.
	(linux_wait_1): Call extended_event_reported.
	(linux_write_memory): Add pid to debug message.
	(reset_lwp_ptrace_options_callback): New function.
	(linux_handle_new_gdb_connection): New function.
	(linux_target_ops): Initialize new structure member.
	(initialize_low): Call linux_ptrace_set_additional_flags.
	* linux-low.h (struct lwp_info) <waitstatus>: New member.
	* lynx-low.c: Initialize new structure member.
	* remote-utils.c (prepare_resume_reply): Implement stop reason
	"xfork" for "T" stop message.
	* server.h (report_fork_events): Declare global flag.
	* target.h (struct target_ops) <handle_new_gdb_connection>:
	New member.
	* win32-low.c: Initialize new structure member.

gdb/
2015-02-25  Don Breazeal  <donb@codesourcery.com>

	* NEWS: Announce follow fork support and associated RSP
	addition.
	* nat/linux-ptrace.c (supported_additional_flags): New
	static variable.
	(linux_check_ptrace_features): Use supported_additional_flags.
	(linux_test_for_tracesysgood): Likewise.
	(linux_test_for_tracefork): Likewise.
	(linux_reset_ptrace_options): New function.
	(linux_ptrace_clear_flags): New function.
	* nat/linux-ptrace.h: (linux_ptrace_clear_flags): Declare new
	function.
	* remote.c (remote_fork_event_p): New function.
	(remote_detach_1): Add target_ops argument, don't mourn inferior
	if doing detach-on-fork.
	(remote_detach): Add target_ops argument, pass to remote_detach_1.
	(extended_remote_detach): Add target_ops argument, pass to
	remote_detach_1.
	(remote_follow_fork): New function.
	(remote_parse_stop_reply): Handle new "T" stop reason "xfork".
	(remote_pid_to_str): Print "process" strings for pid/0/0 ptids.
	(init_extended_remote_ops): Initialize to_follow_fork.

gdb/testsuite/
2015-02-25  Don Breazeal  <donb@codesourcery.com>

	* gdb.base/multi-forks.exp (continue_to_exit_bp_loc):
	Use '-i' in expect statement to include input from forked child.
	Ignore inferior output if gdb,noinferiorio.
	* lib/gdb.exp (server_spawn_id): Declare new global
	variable.
	* lib/gdbserver-support.exp (server_spawn_id): Make
	variable global.


---
 gdb/NEWS                                |  15 ++-
 gdb/doc/gdb.texinfo                     |   9 ++
 gdb/gdbserver/linux-low.c               | 168 +++++++++++++++++++++++++++++---
 gdb/gdbserver/linux-low.h               |   4 +
 gdb/gdbserver/lynx-low.c                |   1 +
 gdb/gdbserver/remote-utils.c            |  15 ++-
 gdb/gdbserver/server.c                  |   3 +
 gdb/gdbserver/server.h                  |   1 +
 gdb/gdbserver/target.h                  |   7 ++
 gdb/gdbserver/win32-low.c               |   1 +
 gdb/linux-nat.c                         |   6 +-
 gdb/nat/linux-ptrace.c                  |  59 +++++++++--
 gdb/nat/linux-ptrace.h                  |  11 +++
 gdb/remote.c                            |  98 ++++++++++++++++---
 gdb/testsuite/gdb.base/multi-forks.exp  |  22 ++++-
 gdb/testsuite/lib/gdb.exp               |   3 +
 gdb/testsuite/lib/gdbserver-support.exp |   2 +
 17 files changed, 382 insertions(+), 43 deletions(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index d7fe430..bd7fac9 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -63,6 +63,10 @@ qXfer:btrace-conf:read
 Qbtrace-conf:bts:size
   Set the requested ring buffer size for branch tracing in BTS format.
 
+T Stop Reply Packet's reason
+  The T stop reply packet supports a new stop reason 'xfork', which
+  signifies that the specified inferior has executed a fork.
+
 * The info record command now shows the recording format and the
   branch tracing configuration for the current thread when using
   the btrace record target.
@@ -71,8 +75,15 @@ Qbtrace-conf:bts:size
 * GDB now has support for DTrace USDT (Userland Static Defined
   Tracing) probes.  The supported targets are x86_64-*-linux-gnu.
 
-* The remote stub now reports support for fork and vfork events to GDB's
-qSupported query.
+* Remote fork events
+
+  The remote stub now reports support for fork and vfork events to
+  GDB's qSupported query.
+
+  GDBserver extended-remote Linux targets now provides basic support
+  for fork events.  This enables follow-fork-mode and detach-on-fork
+  for those targets with Linux kernels 2.5.60 and later.  Hardware
+  watchpoints are not inherited across a fork in this implementation.
 
 *** Changes in GDB 7.9
 
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 9834709..9b685bc 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -3142,6 +3142,9 @@ create additional processes using the @code{fork} or @code{vfork} functions.
 Currently, the only platforms with this feature are HP-UX (11.x and later
 only?) and @sc{gnu}/Linux (kernel version 2.5.60 and later).
 
+The fork debugging commands are supported in both native mode and when
+connected to @code{gdbserver} using @kbd{target extended-remote}.
+
 By default, when a program forks, @value{GDBN} will continue to debug
 the parent process and the child process will run unimpeded.
 
@@ -35181,6 +35184,12 @@ The packet indicates that the loaded libraries have changed.
 @value{GDBN} should use @samp{qXfer:libraries:read} to fetch a new
 list of loaded libraries.  The @var{r} part is ignored.
 
+@cindex fork events, remote reply
+@item xfork
+The packet indicates that @code{fork} was called, and @var{r}
+is the ptid of the new child process.  This packet is only
+applicable to targets that support fork events.
+
 @cindex replay log events, remote reply
 @item replaylog
 The packet indicates that the target cannot continue replaying 
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 5eb7576..d26282b 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -20,6 +20,7 @@
 #include "linux-low.h"
 #include "nat/linux-osdata.h"
 #include "agent.h"
+#include "tdesc.h"
 
 #include "nat/linux-nat.h"
 #include "nat/linux-waitpid.h"
@@ -371,22 +372,23 @@ linux_add_process (int pid, int attached)
 static CORE_ADDR get_pc (struct lwp_info *lwp);
 
 /* Handle a GNU/Linux extended wait response.  If we see a clone
-   event, we need to add the new LWP to our list (and not report the
-   trap to higher layers).  */
+   event, we need to add the new LWP to our list (and return 0 so as
+   not to report the trap to higher layers).  */
 
-static void
+static int
 handle_extended_wait (struct lwp_info *event_child, int wstat)
 {
   int event = linux_ptrace_get_extended_event (wstat);
   struct thread_info *event_thr = get_lwp_thread (event_child);
   struct lwp_info *new_lwp;
 
-  if (event == PTRACE_EVENT_CLONE)
+  if ((event == PTRACE_EVENT_FORK) || (event == PTRACE_EVENT_CLONE))
     {
       ptid_t ptid;
       unsigned long new_pid;
       int ret, status;
 
+      /* Get the pid of the new lwp.  */
       ptrace (PTRACE_GETEVENTMSG, lwpid_of (event_thr), (PTRACE_TYPE_ARG3) 0,
 	      &new_pid);
 
@@ -406,6 +408,59 @@ handle_extended_wait (struct lwp_info *event_child, int wstat)
 	    warning ("wait returned unexpected status 0x%x", status);
 	}
 
+      if (event == PTRACE_EVENT_FORK)
+	{
+	  struct process_info *parent_proc;
+	  struct process_info *child_proc;
+	  struct lwp_info *child_lwp;
+	  struct lwp_info *parent_lwp;
+	  struct target_desc *tdesc;
+
+	  ptid = ptid_build (new_pid, new_pid, 0);
+
+	  if (debug_threads)
+	    {
+	      debug_printf ("HEW: Got fork event from LWP %ld, "
+			    "new child is %d\n",
+			    ptid_get_lwp (ptid_of (event_thr)),
+			    ptid_get_pid (ptid));
+	    }
+
+	  /* Add the new process to the tables and clone the breakpoint
+	     lists of the parent.  We need to do this even if the new process
+	     will be detached, since we will need the process object and the
+	     breakpoints to remove any breakpoints from memory when we
+	     detach, and the host side will access registers.  */
+	  child_proc = linux_add_process (new_pid, 0);
+	  gdb_assert (child_proc != NULL);
+	  child_lwp = add_lwp (ptid);
+	  gdb_assert (child_lwp != NULL);
+	  child_lwp->stopped = 1;
+	  parent_proc = get_thread_process (event_thr);
+	  child_proc->attached = parent_proc->attached;
+	  clone_all_breakpoints (&child_proc->breakpoints,
+				 &child_proc->raw_breakpoints,
+				 parent_proc->breakpoints);
+
+	  tdesc = xmalloc (sizeof (struct target_desc));
+	  copy_target_description (tdesc, parent_proc->tdesc);
+	  child_proc->tdesc = tdesc;
+	  child_lwp->must_set_ptrace_flags = 1;
+
+	  /* Save fork info in the parent thread.  */
+	  parent_lwp = get_thread_lwp (event_thr);
+	  parent_lwp->waitstatus.kind = TARGET_WAITKIND_FORKED;
+	  parent_lwp->waitstatus.value.related_pid = ptid;
+	  /* The status_pending field contains bits denoting the
+	     extended event, so when the pending event is handled,
+	     the handler will look at lwp->waitstatus.  */
+	  parent_lwp->status_pending_p = 1;
+	  parent_lwp->status_pending = wstat;
+
+	  /* Report the event.  */
+	  return 0;
+	}
+
       if (debug_threads)
 	debug_printf ("HEW: Got clone event "
 		      "from LWP %ld, new child is LWP %ld\n",
@@ -434,7 +489,12 @@ handle_extended_wait (struct lwp_info *event_child, int wstat)
 	  new_lwp->status_pending_p = 1;
 	  new_lwp->status_pending = status;
 	}
+
+      /* Don't report the event.  */
+      return 1;
     }
+
+  internal_error (__FILE__, __LINE__, _("unknown ptrace event %d"), event);
 }
 
 /* Return the PC as read from the regcache of LWP, without any
@@ -1773,6 +1833,19 @@ check_stopped_by_watchpoint (struct lwp_info *child)
   return child->stop_reason == LWP_STOPPED_BY_WATCHPOINT;
 }
 
+/* Wrapper for linux_enable_event_reporting that disables any
+   supported events that we have determined should not be
+   reported (e.g. GDB did not request them).  */
+
+static void
+linux_low_enable_events (pid_t pid, int attached)
+{
+  if (!report_fork_events)
+    linux_ptrace_clear_flags (PTRACE_O_TRACEFORK);
+
+  linux_enable_event_reporting (pid, attached);
+}
+
 /* Do low-level handling of the event, and check if we should go on
    and pass it to caller code.  Return the affected lwp if we are, or
    NULL otherwise.  */
@@ -1857,11 +1930,11 @@ linux_low_filter_event (int lwpid, int wstat)
 	}
     }
 
-  if (WIFSTOPPED (wstat) && child->must_set_ptrace_flags)
+  if (WIFSTOPPED (wstat) && child->must_set_ptrace_flags && gdb_connected ())
     {
       struct process_info *proc = find_process_pid (pid_of (thread));
 
-      linux_enable_event_reporting (lwpid, proc->attached);
+      linux_low_enable_events (lwpid, proc->attached);
       child->must_set_ptrace_flags = 0;
     }
 
@@ -1871,8 +1944,12 @@ linux_low_filter_event (int lwpid, int wstat)
       && linux_is_extended_waitstatus (wstat))
     {
       child->stop_pc = get_pc (child);
-      handle_extended_wait (child, wstat);
-      return NULL;
+      if (handle_extended_wait (child, wstat))
+	{
+	  /* The event has been handled, so just return without
+	     reporting it.  */
+	  return NULL;
+	}
     }
 
   if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) == SIGTRAP
@@ -2438,6 +2515,18 @@ ignore_event (struct target_waitstatus *ourstatus)
   return null_ptid;
 }
 
+/* Return non-zero if WAITSTATUS reflects an extended linux
+   event.  Otherwise, return zero.  */
+
+static int
+extended_event_reported (const struct target_waitstatus *waitstatus)
+{
+  if (waitstatus == NULL)
+    return 0;
+
+  return (waitstatus->kind == TARGET_WAITKIND_FORKED);
+}
+
 /* Wait for process, returns status.  */
 
 static ptid_t
@@ -2809,7 +2898,8 @@ linux_wait_1 (ptid_t ptid,
 		       && !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);
 
@@ -2831,6 +2921,13 @@ linux_wait_1 (ptid_t ptid,
 			  paddress (event_child->stop_pc),
 			  paddress (event_child->step_range_start),
 			  paddress (event_child->step_range_end));
+	  if (extended_event_reported (&event_child->waitstatus))
+	    {
+	      char *str = target_waitstatus_to_string (ourstatus);
+	      debug_printf ("LWP %ld: extended event 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
@@ -2940,7 +3037,17 @@ linux_wait_1 (ptid_t ptid,
 	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 lwp's waitstatus since we handled it already.  */
+      event_child->waitstatus.kind = TARGET_WAITKIND_SPURIOUS;
+    }
+  else
+    ourstatus->kind = TARGET_WAITKIND_STOPPED;
 
   /* Now that we've selected our final event LWP, un-adjust its PC if
      it was a software breakpoint.  */
@@ -2971,7 +3078,7 @@ linux_wait_1 (ptid_t ptid,
 	 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));
     }
@@ -4769,8 +4876,8 @@ linux_write_memory (CORE_ADDR memaddr, const unsigned char *myaddr, int len)
 	val = val & 0xffff;
       else if (len == 3)
 	val = val & 0xffffff;
-      debug_printf ("Writing %0*x to 0x%08lx\n", 2 * ((len < 4) ? len : 4),
-		    val, (long)memaddr);
+      debug_printf ("Writing %0*x to 0x%08lx in process %d\n",
+		    2 * ((len < 4) ? len : 4), val, (long)memaddr, pid);
     }
 
   /* Fill start and end extra bytes of buffer with existing memory data.  */
@@ -5170,6 +5277,39 @@ linux_supports_vfork_events (void)
   return linux_supports_tracefork ();
 }
 
+/* Callback for 'find_inferior'.  Set the (possibly changed) ptrace
+   options for the specified lwp.  */
+
+static int
+reset_lwp_ptrace_options_callback (struct inferior_list_entry *entry,
+				   void *args)
+{
+  struct thread_info *thread = (struct thread_info *) entry;
+  struct lwp_info *lwp = get_thread_lwp (thread);
+  struct process_info *proc = find_process_pid (pid_of (thread));
+
+  linux_low_enable_events (lwpid_of (thread), proc->attached);
+  lwp->must_set_ptrace_flags = 0;
+
+  return 0;
+}
+
+/* Target hook for 'handle_new_gdb_connection'.  Causes a reset of the
+   ptrace flags for all inferiors.  This is in case the new GDB connection
+   doesn't support the same set of events that the previous one did.  */
+
+static void
+linux_handle_new_gdb_connection (void)
+{
+  pid_t pid;
+
+  /* Reset the ptrace options to enable on the inferior(s).  */
+  linux_reset_ptrace_options ();
+
+  /* Request that all the lwps reset their ptrace options.  */
+  find_inferior (&all_threads, reset_lwp_ptrace_options_callback , &pid);
+}
+
 static int
 linux_supports_disable_randomization (void)
 {
@@ -6132,6 +6272,7 @@ static struct target_ops linux_target_ops = {
   linux_supports_multi_process,
   linux_supports_fork_events,
   linux_supports_vfork_events,
+  linux_handle_new_gdb_connection,
 #ifdef USE_THREAD_DB
   thread_db_handle_monitor_command,
 #else
@@ -6210,5 +6351,6 @@ initialize_low (void)
   initialize_low_arch ();
 
   /* Enable extended ptrace events.  */
+  linux_ptrace_set_additional_flags (PTRACE_O_TRACEFORK);
   linux_check_ptrace_features ();
 }
diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h
index bea4a37..0a53eaf 100644
--- a/gdb/gdbserver/linux-low.h
+++ b/gdb/gdbserver/linux-low.h
@@ -287,6 +287,10 @@ struct lwp_info
   /* When stopped is set, the last wait status recorded for this lwp.  */
   int last_status;
 
+  /* This is used to store extended ptrace event information until
+     it is reported to GDB.  */
+  struct target_waitstatus waitstatus;
+
   /* When stopped is set, this is where the lwp last stopped, with
      decr_pc_after_break already accounted for.  If the LWP is
      running, this is the address at which the lwp was resumed.  */
diff --git a/gdb/gdbserver/lynx-low.c b/gdb/gdbserver/lynx-low.c
index 1126103..64a5bf3 100644
--- a/gdb/gdbserver/lynx-low.c
+++ b/gdb/gdbserver/lynx-low.c
@@ -756,6 +756,7 @@ static struct target_ops lynx_target_ops = {
   NULL,  /* supports_multi_process */
   NULL,  /* supports_fork_events */
   NULL,  /* supports_vfork_events */
+  NULL,  /* handle_new_gdb_connection */
   NULL,  /* handle_monitor_command */
 };
 
diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c
index 8854c4c..6191e58 100644
--- a/gdb/gdbserver/remote-utils.c
+++ b/gdb/gdbserver/remote-utils.c
@@ -1114,12 +1114,25 @@ prepare_resume_reply (char *buf, ptid_t ptid,
   switch (status->kind)
     {
     case TARGET_WAITKIND_STOPPED:
+    case TARGET_WAITKIND_FORKED:
       {
 	struct thread_info *saved_thread;
 	const char **regp;
 	struct regcache *regcache;
 
-	sprintf (buf, "T%02x", status->value.sig);
+	if (status->kind == TARGET_WAITKIND_FORKED && report_fork_events)
+	  {
+	    enum gdb_signal signal = GDB_SIGNAL_TRAP;
+	    const char *event = "xfork";
+
+	    sprintf (buf, "T%02x%s:", signal, event);
+	    buf += strlen (buf);
+	    buf = write_ptid (buf, status->value.related_pid);
+	    strcat (buf, ";");
+	  }
+	else
+	  sprintf (buf, "T%02x", status->value.sig);
+
 	buf += strlen (buf);
 
 	saved_thread = current_thread;
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index 1efcbc0..19e3ba4 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -2081,6 +2081,9 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
 
       supported_btrace_packets (own_buf);
 
+      /* Reinitialize the target as needed for the new connection.  */
+      target_handle_new_gdb_connection ();
+
       return;
     }
 
diff --git a/gdb/gdbserver/server.h b/gdb/gdbserver/server.h
index dbf31d5..ce6ffb9 100644
--- a/gdb/gdbserver/server.h
+++ b/gdb/gdbserver/server.h
@@ -84,6 +84,7 @@ extern int disable_packet_qfThreadInfo;
 
 extern int run_once;
 extern int multi_process;
+extern int report_fork_events;
 extern int non_stop;
 
 extern int disable_randomization;
diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
index 74ed07c..1911584 100644
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -268,6 +268,9 @@ struct target_ops
   /* Returns true if vfork events are supported.  */
   int (*supports_vfork_events) (void);
 
+  /* Allows target to re-initialize connection-specific settings.  */
+  void (*handle_new_gdb_connection) (void);
+
   /* If not NULL, target-specific routine to process monitor command.
      Returns 1 if handled, or 0 to perform default processing.  */
   int (*handle_monitor_command) (char *);
@@ -407,6 +410,10 @@ int kill_inferior (int);
   (the_target->supports_vfork_events ? \
    (*the_target->supports_vfork_events) () : 0)
 
+#define target_handle_new_gdb_connection() \
+  (the_target->handle_new_gdb_connection ? \
+   (*the_target->handle_new_gdb_connection) () : 0)
+
 #define detach_inferior(pid) \
   (*the_target->detach) (pid)
 
diff --git a/gdb/gdbserver/win32-low.c b/gdb/gdbserver/win32-low.c
index 210a747..fa6c455 100644
--- a/gdb/gdbserver/win32-low.c
+++ b/gdb/gdbserver/win32-low.c
@@ -1825,6 +1825,7 @@ static struct target_ops win32_target_ops = {
   NULL, /* supports_multi_process */
   NULL, /* supports_fork_events */
   NULL, /* supports_vfork_events */
+  NULL, /* handle_new_gdb_connection */
   NULL, /* handle_monitor_command */
   NULL, /* core_of_thread */
   NULL, /* read_loadmap */
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index 2e1133d..2f93951 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -4804,11 +4804,7 @@ Enables printf debugging output."),
 
   /* Do not enable PTRACE_O_TRACEEXIT until GDB is more prepared to
      support read-only process state.  */
-  linux_ptrace_set_additional_flags (PTRACE_O_TRACESYSGOOD
-				     | PTRACE_O_TRACEVFORKDONE
-				     | PTRACE_O_TRACEVFORK
-				     | PTRACE_O_TRACEFORK
-				     | PTRACE_O_TRACEEXEC);
+  linux_ptrace_set_additional_flags (all_additional_flags);
 }
 \f
 
diff --git a/gdb/nat/linux-ptrace.c b/gdb/nat/linux-ptrace.c
index ba934af..55fd20d 100644
--- a/gdb/nat/linux-ptrace.c
+++ b/gdb/nat/linux-ptrace.c
@@ -25,13 +25,16 @@
 
 #include <stdint.h>
 
-/* Stores the currently supported ptrace options.  A value of
-   -1 means we did not check for features yet.  A value of 0 means
-   there are no supported features.  */
+/* Stores the ptrace options currently enabled by this gdbserver.
+   A value of -1 means we did not check for features yet.  A value
+   of 0 means there are no enabled features.  */
 static int current_ptrace_options = -1;
 
-/* Additional flags to test.  */
+/* Stores the fork and exec ptrace options supported by the operating
+   system.  */
+static int supported_additional_flags = 0;
 
+/* Additional flags to test.  */
 static int additional_flags;
 
 /* Find all possible reasons we could fail to attach PID and append
@@ -344,6 +347,7 @@ linux_check_ptrace_features (void)
 
   /* Initialize the options.  */
   current_ptrace_options = 0;
+  supported_additional_flags = 0;
 
   /* Fork a child so we can do some testing.  The child will call
      linux_child_function and will get traced.  The child will
@@ -394,7 +398,7 @@ linux_test_for_tracesysgood (int child_pid)
 		(PTRACE_TYPE_ARG4) PTRACE_O_TRACESYSGOOD);
 
   if (ret == 0)
-    current_ptrace_options |= PTRACE_O_TRACESYSGOOD;
+    supported_additional_flags |= PTRACE_O_TRACESYSGOOD;
 }
 
 /* Determine if PTRACE_O_TRACEFORK can be used to follow fork
@@ -421,7 +425,7 @@ linux_test_for_tracefork (int child_pid)
 		    (PTRACE_TYPE_ARG4) (PTRACE_O_TRACEFORK
 					| PTRACE_O_TRACEVFORKDONE));
       if (ret == 0)
-	current_ptrace_options |= PTRACE_O_TRACEVFORKDONE;
+	supported_additional_flags |= PTRACE_O_TRACEVFORKDONE;
     }
 
   /* Setting PTRACE_O_TRACEFORK did not cause an error, however we
@@ -459,9 +463,16 @@ linux_test_for_tracefork (int child_pid)
 	  /* We got the PID from the grandchild, which means fork
 	     tracing is supported.  */
 	  current_ptrace_options |= PTRACE_O_TRACECLONE;
-	  current_ptrace_options |= (additional_flags & (PTRACE_O_TRACEFORK
-                                                         | PTRACE_O_TRACEVFORK
-                                                         | PTRACE_O_TRACEEXEC));
+
+	  /* Save the "extended" options in case we need to reset
+	     the options later for a connect from a different GDB.  */
+	  supported_additional_flags |= (PTRACE_O_TRACEFORK
+					  | PTRACE_O_TRACEVFORK
+					  | PTRACE_O_TRACEEXEC);
+
+	  /* Enable only those options requested by GDB.  */
+	  current_ptrace_options |= (supported_additional_flags
+				     & additional_flags);
 
 	  /* Do some cleanup and kill the grandchild.  */
 	  my_waitpid (second_pid, &second_status, 0);
@@ -518,6 +529,25 @@ linux_enable_event_reporting (pid_t pid, int attached)
 	  (PTRACE_TYPE_ARG4) (uintptr_t) ptrace_options);
 }
 
+/* Reset the ptrace options using potentially different
+   additional_flags.  */
+
+void
+linux_reset_ptrace_options (void)
+{
+  /* Check if we have initialized the ptrace features for this
+     target.  If not, do it now.  */
+  if (current_ptrace_options == -1)
+    linux_check_ptrace_features ();
+
+  /* Clear only the "extended" options.  */
+  linux_ptrace_clear_flags (all_additional_flags);
+
+  /* Add the new requested flags back in.  */
+  current_ptrace_options |= (supported_additional_flags
+			     & additional_flags);
+}
+
 /* Disable reporting of all currently supported ptrace events.  */
 
 void
@@ -603,9 +633,20 @@ linux_ptrace_init_warnings (void)
 void
 linux_ptrace_set_additional_flags (int flags)
 {
+  int invalid_flags = (flags & ~all_additional_flags);
+
+  gdb_assert (invalid_flags == 0);
   additional_flags = flags;
 }
 
+/* Clear FLAGS in current_ptrace_options.  */
+
+void
+linux_ptrace_clear_flags (int flags)
+{
+  current_ptrace_options &= ~flags;
+}
+
 /* Extract extended ptrace event from wait status.  */
 
 int
diff --git a/gdb/nat/linux-ptrace.h b/gdb/nat/linux-ptrace.h
index c7bcfb0..4d772ee 100644
--- a/gdb/nat/linux-ptrace.h
+++ b/gdb/nat/linux-ptrace.h
@@ -88,6 +88,15 @@ struct buffer;
 #define __WALL          0x40000000 /* Wait for any child.  */
 #endif
 
+/* All of the additional ptrace flags.  The flags passed to
+   linux_ptrace_set_additional_flags must be in
+   this set of flags.  */
+static const int all_additional_flags = (PTRACE_O_TRACEFORK
+					 | PTRACE_O_TRACEVFORK
+					 | PTRACE_O_TRACEVFORKDONE
+					 | PTRACE_O_TRACEEXEC
+					 | PTRACE_O_TRACESYSGOOD);
+
 extern void linux_ptrace_attach_fail_reason (pid_t pid, struct buffer *buffer);
 
 /* Find all possible reasons we could have failed to attach to PTID
@@ -100,12 +109,14 @@ extern char *linux_ptrace_attach_fail_reason_string (ptid_t ptid, int err);
 extern void linux_ptrace_init_warnings (void);
 extern void linux_check_ptrace_features (void);
 extern void linux_enable_event_reporting (pid_t pid, int attached);
+extern void linux_reset_ptrace_options (void);
 extern void linux_disable_event_reporting (pid_t pid);
 extern int linux_supports_tracefork (void);
 extern int linux_supports_traceclone (void);
 extern int linux_supports_tracevforkdone (void);
 extern int linux_supports_tracesysgood (void);
 extern void linux_ptrace_set_additional_flags (int);
+extern void linux_ptrace_clear_flags (int flags);
 extern int linux_ptrace_get_extended_event (int wstat);
 extern int linux_is_extended_waitstatus (int wstat);
 extern int linux_wstatus_maybe_breakpoint (int wstat);
diff --git a/gdb/remote.c b/gdb/remote.c
index 50b6d45..3a6c6cb 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -1449,6 +1449,14 @@ remote_multi_process_p (struct remote_state *rs)
   return packet_support (PACKET_multiprocess_feature) == PACKET_ENABLE;
 }
 
+/* Returns true if fork events are supported.  */
+
+static int
+remote_fork_event_p (struct remote_state *rs)
+{
+  return packet_support (PACKET_fork_event_feature) == PACKET_ENABLE;
+}
+
 /* Tokens for use by the asynchronous signal handlers for SIGINT.  */
 static struct async_signal_handler *async_sigint_remote_twice_token;
 static struct async_signal_handler *async_sigint_remote_token;
@@ -4395,16 +4403,22 @@ remote_open_1 (const char *name, int from_tty,
     wait_forever_enabled_p = 1;
 }
 
-/* This takes a program previously attached to and detaches it.  After
-   this is done, GDB can be used to debug some other program.  We
-   better not have left any breakpoints in the target program or it'll
-   die when it hits one.  */
+/* This detaches a program to which we previously attached, using
+   inferior_ptid to identify the process.  After this is done, GDB
+   can be used to debug some other program.  We better not have left
+   any breakpoints in the target program or it'll die when it hits
+   one.  If IS_FORK_CHILD is true, then inferior_ptid is the child
+   of an unfollowed fork, and we need to avoid deleting breakpoints
+   still needed by the parent.  */
 
 static void
-remote_detach_1 (const char *args, int from_tty, int extended)
+remote_detach_1 (struct target_ops *ops, const char *args,
+		 int from_tty, int is_fork_child)
 {
   int pid = ptid_get_pid (inferior_ptid);
   struct remote_state *rs = get_remote_state ();
+  struct thread_info *tp = find_thread_ptid (inferior_ptid);
+  int is_fork_parent;
 
   if (args)
     error (_("Argument given to \"detach\" when remotely debugging."));
@@ -4438,22 +4452,75 @@ remote_detach_1 (const char *args, int from_tty, int extended)
   else
     error (_("Can't detach process."));
 
-  if (from_tty && !extended)
+  if (from_tty && !rs->extended)
     puts_filtered (_("Ending remote debugging.\n"));
 
-  target_mourn_inferior ();
+  /* Check to see if we are detaching a fork parent.  Note that if we
+     are detaching a fork child, tp == NULL.  */
+  if (tp != NULL)
+    is_fork_parent = tp->pending_follow.kind == TARGET_WAITKIND_FORKED;
+
+  /* If doing detach-on-fork, we don't mourn, because that will delete
+     breakpoints that should be available for the followed inferior.  */
+  if (!is_fork_child && !is_fork_parent)
+    target_mourn_inferior ();
+  else
+    {
+      inferior_ptid = null_ptid;
+      detach_inferior (pid);
+    }
 }
 
 static void
 remote_detach (struct target_ops *ops, const char *args, int from_tty)
 {
-  remote_detach_1 (args, from_tty, 0);
+  remote_detach_1 (ops, args, from_tty, 0);
 }
 
 static void
 extended_remote_detach (struct target_ops *ops, const char *args, int from_tty)
 {
-  remote_detach_1 (args, from_tty, 1);
+  remote_detach_1 (ops, args, from_tty, 0);
+}
+
+/* Target follow-fork function for remote targets.  On entry, and
+   at return, the current inferior is the fork parent.
+
+   Note that although this is currently only used for extended-remote,
+   it is named remote_follow_fork in anticipation of using it for the
+   remote target as well.  */
+
+static int
+remote_follow_fork (struct target_ops *ops, int follow_child,
+		    int detach_fork)
+{
+  struct remote_state *rs = get_remote_state ();
+
+  if (remote_fork_event_p (rs))
+    {
+      if (detach_fork && !follow_child)
+	{
+	  ptid_t parent_ptid;
+	  ptid_t child_ptid;
+
+	  gdb_assert (inferior_thread ()->pending_follow.kind
+		      == TARGET_WAITKIND_FORKED);
+
+	  /* remote_detach_1 detaches inferior_ptid, which is currently
+	     the ptid of the parent.  Switch inferior_ptid to the ptid
+	     of the child for the duration of the call.  */
+	  parent_ptid = inferior_ptid;
+	  child_ptid = inferior_thread ()->pending_follow.value.related_pid;
+	  inferior_ptid = child_ptid;
+	  gdb_assert (!ptid_equal (parent_ptid, child_ptid));
+
+	  remote_detach_1 (ops, NULL, 0, 1);
+
+	  /* Restore the parent ptid.  */
+	  inferior_ptid = parent_ptid;
+	}
+    }
+  return 0;
 }
 
 /* Same as remote_detach, but don't send the "D" packet; just disconnect.  */
@@ -5595,6 +5662,11 @@ Packet: '%s'\n"),
 	      p = unpack_varlen_hex (++p1, &c);
 	      event->core = c;
 	    }
+	  else if (strncmp (p, "xfork", p1 - p) == 0)
+	    {
+	      event->ws.value.related_pid = read_ptid (++p1, &p);
+	      event->ws.kind = TARGET_WAITKIND_FORKED;
+	    }
 	  else
 	    {
 	      ULONGEST pnum;
@@ -9413,8 +9485,11 @@ remote_pid_to_str (struct target_ops *ops, ptid_t ptid)
       if (ptid_equal (magic_null_ptid, ptid))
 	xsnprintf (buf, sizeof buf, "Thread <main>");
       else if (rs->extended && remote_multi_process_p (rs))
-	xsnprintf (buf, sizeof buf, "Thread %d.%ld",
-		   ptid_get_pid (ptid), ptid_get_lwp (ptid));
+	if (ptid_get_lwp (ptid) == 0)
+	  return normal_pid_to_str (ptid);
+	else
+	  xsnprintf (buf, sizeof buf, "Thread %d.%ld",
+		     ptid_get_pid (ptid), ptid_get_lwp (ptid));
       else
 	xsnprintf (buf, sizeof buf, "Thread %ld",
 		   ptid_get_lwp (ptid));
@@ -11758,6 +11833,7 @@ Specify the serial device it is connected to (e.g. /dev/ttya).";
   extended_remote_ops.to_kill = extended_remote_kill;
   extended_remote_ops.to_supports_disable_randomization
     = extended_remote_supports_disable_randomization;
+  extended_remote_ops.to_follow_fork = remote_follow_fork;
 }
 
 static int
diff --git a/gdb/testsuite/gdb.base/multi-forks.exp b/gdb/testsuite/gdb.base/multi-forks.exp
index e95cb4b..2a71e63 100644
--- a/gdb/testsuite/gdb.base/multi-forks.exp
+++ b/gdb/testsuite/gdb.base/multi-forks.exp
@@ -62,6 +62,23 @@ proc continue_to_exit_bp_loc {} {
     set seen_break 0
     set seen_prompt 0
     set seen_timeout 0
+
+    # If we are running with a native gdbserver, the output ($decimal done)
+    # will come via the spawn_id of gdbserver, not the spawn_id of gdb (the
+    # default).  So we grab the spawn_id of gdbserver, if it exists, and
+    # add it to the gdb_expect statement below using "-i", allowing us to
+    # apply the expect statement to the output of both spawn_ids.
+    #
+    # If we are running with a truly remote gdbserver (gdb,noinferiorio),
+    # the output of the program will be inaccessible.  In this case we do
+    # not check for the ($decimal done) output, but just look for the gdb
+    # prompt.
+    global server_spawn_id
+    set current_spawn_id [board_info host fileid]
+    if {![info exists server_spawn_id]} {
+	set server_spawn_id ""
+    }
+
     while { ($seen_done < 16 || ! $seen_prompt) && ! $seen_timeout } {
 	# We don't know what order the interesting things will arrive in.
 	# Using a pattern of the form 'x|y|z' instead of -re x ... -re y
@@ -70,7 +87,8 @@ proc continue_to_exit_bp_loc {} {
 	# first in the script that occurs anywhere in the input, so that
 	# we don't skip anything.
 	gdb_expect {
-	    -re "($decimal done)|(Breakpoint)|($gdb_prompt)" {
+	    -i "$current_spawn_id $server_spawn_id" \
+	         -re "($decimal done)|(Breakpoint)|($gdb_prompt)" {
 		if {[info exists expect_out(1,string)]} {
 		    incr seen_done
 		} elseif {[info exists expect_out(2,string)]} {
@@ -90,7 +108,7 @@ proc continue_to_exit_bp_loc {} {
 	fail "run to exit 2 (no prompt)"
     } elseif { ! $seen_break } {
 	fail "run to exit 2 (no breakpoint hit)"
-    } elseif { $seen_done != 16 } {
+    } elseif {![target_info exists gdb,noinferiorio] && $seen_done != 16 } {
 	fail "run to exit 2 (missing done messages)"
     } else {
 	pass "run to exit 2"
diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index 08db806..cc29ad2 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -29,6 +29,9 @@ load_lib libgloss.exp
 load_lib cache.exp
 load_lib gdb-utils.exp
 
+# Stores the expect spawn ID of gdbserver.
+global server_spawn_id
+
 global GDB
 
 if [info exists TOOL_EXECUTABLE] {
diff --git a/gdb/testsuite/lib/gdbserver-support.exp b/gdb/testsuite/lib/gdbserver-support.exp
index 4a59154..86c6e35 100644
--- a/gdb/testsuite/lib/gdbserver-support.exp
+++ b/gdb/testsuite/lib/gdbserver-support.exp
@@ -270,6 +270,8 @@ proc gdbserver_start { options arguments } {
 	    append gdbserver_command " $arguments"
 	}
 
+	# Save the gdbserver spawn ID.
+	global server_spawn_id
 	set server_spawn_id [remote_spawn target $gdbserver_command]
 
 	# Wait for the server to open its TCP socket, so that GDB can connect.
-- 
1.8.1.1

^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH v5 02/06] Clone remote breakpoints
  2015-02-27  0:46   ` [PATCH v5 0/6] " Don Breazeal
  2015-02-27  0:46     ` [PATCH v5 01/06] Identify remote fork event support Don Breazeal
  2015-02-27  0:47     ` [PATCH v5 04/06] Arch-specific remote follow fork Don Breazeal
@ 2015-02-27  0:47     ` Don Breazeal
  2015-02-27  0:47     ` [PATCH v5 03/06] Extended-remote Linux follow fork Don Breazeal
                       ` (5 subsequent siblings)
  8 siblings, 0 replies; 30+ messages in thread
From: Don Breazeal @ 2015-02-27  0:47 UTC (permalink / raw)
  To: gdb-patches, palves

This version of this patch is unchanged except possibly for merges from
the mainline.  The last review/response was here:
https://sourceware.org/ml/gdb-patches/2014-10/msg00883.html.

Thanks,
--Don

This patch implements gdbserver routines to clone the breakpoint lists of a
process, duplicating them for another process.  In gdbserver, each process
maintains its own independent breakpoint list.  When a fork call creates a
child, all of the breakpoints currently inserted in the parent process are
also inserted in the child process, but there is nothing to describe them
in the data structures related to the child.  The child must have a
breakpoint list describing them so that they can be removed (if detaching)
or recognized (if following).  Implementation is a mechanical process of
just cloning the lists in several new functions in gdbserver/mem-break.c.

Tested by building, since none of the new functions are called yet.  This
was tested with another patch in the series that implements follow-fork.

gdb/gdbserver/
2015-01-25  Don Breazeal  <donb@codesourcery.com>

        * mem-break.c (APPEND_TO_LIST): Define macro.
        (clone_agent_expr): New function.
        (clone_one_breakpoint): New function.
        (clone_all_breakpoints): New function.
        * mem-break.h: Declare new functions.

---
 gdb/gdbserver/mem-break.c | 105 ++++++++++++++++++++++++++++++++++++++++++++++
 gdb/gdbserver/mem-break.h |   6 +++
 2 files changed, 111 insertions(+)

diff --git a/gdb/gdbserver/mem-break.c b/gdb/gdbserver/mem-break.c
index 70fab2e..d1b66bf 100644
--- a/gdb/gdbserver/mem-break.c
+++ b/gdb/gdbserver/mem-break.c
@@ -28,6 +28,24 @@ int breakpoint_len;
 
 #define MAX_BREAKPOINT_LEN 8
 
+/* Helper macro used in loops that append multiple items to a singly-linked
+   list instead of inserting items at the head of the list, as, say, in the
+   breakpoint lists.  LISTPP is a pointer to the pointer that is the head of
+   the new list.  ITEMP is a pointer to the item to be added to the list.
+   TAILP must be defined to be the same type as ITEMP, and initialized to
+   NULL.  */
+
+#define APPEND_TO_LIST(listpp, itemp, tailp) \
+	  do \
+	    { \
+	      if ((tailp) == NULL) \
+		*(listpp) = (itemp); \
+	      else \
+		(tailp)->next = (itemp); \
+	      (tailp) = (itemp); \
+	    } \
+	  while (0)
+
 /* GDB will never try to install multiple breakpoints at the same
    address.  However, we can see GDB requesting to insert a breakpoint
    at an address is had already inserted one previously in a few
@@ -1913,3 +1931,90 @@ free_all_breakpoints (struct process_info *proc)
   while (proc->breakpoints)
     delete_breakpoint_1 (proc, proc->breakpoints);
 }
+
+/* Clone an agent expression.  */
+
+static struct agent_expr *
+clone_agent_expr (const struct agent_expr *src_ax)
+{
+  struct agent_expr *ax;
+
+  ax = xcalloc (1, sizeof (*ax));
+  ax->length = src_ax->length;
+  ax->bytes = xcalloc (ax->length, 1);
+  memcpy (ax->bytes, src_ax->bytes, ax->length);
+  return ax;
+}
+
+/* Deep-copy the contents of one breakpoint to another.  */
+
+static struct breakpoint *
+clone_one_breakpoint (const struct breakpoint *src)
+{
+  struct breakpoint *dest;
+  struct raw_breakpoint *dest_raw;
+  struct point_cond_list *current_cond;
+  struct point_cond_list *new_cond;
+  struct point_cond_list *cond_tail = NULL;
+  struct point_command_list *current_cmd;
+  struct point_command_list *new_cmd;
+  struct point_command_list *cmd_tail = NULL;
+
+  /* Clone the raw breakpoint.  */
+  dest_raw = xcalloc (1, sizeof (*dest_raw));
+  dest_raw->raw_type = src->raw->raw_type;
+  dest_raw->refcount = src->raw->refcount;
+  dest_raw->pc = src->raw->pc;
+  dest_raw->size = src->raw->size;
+  memcpy (dest_raw->old_data, src->raw->old_data, MAX_BREAKPOINT_LEN);
+  dest_raw->inserted = src->raw->inserted;
+
+  /* Clone the high-level breakpoint.  */
+  dest = xcalloc (1, sizeof (*dest));
+  dest->type = src->type;
+  dest->raw = dest_raw;
+  dest->handler = src->handler;
+
+  /* Clone the condition list.  */
+  for (current_cond = src->cond_list; current_cond != NULL;
+       current_cond = current_cond->next)
+    {
+      new_cond = xcalloc (1, sizeof (*new_cond));
+      new_cond->cond = clone_agent_expr (current_cond->cond);
+      APPEND_TO_LIST (&dest->cond_list, new_cond, cond_tail);
+    }
+
+  /* Clone the command list.  */
+  for (current_cmd = src->command_list; current_cmd != NULL;
+       current_cmd = current_cmd->next)
+    {
+      new_cmd = xcalloc (1, sizeof (*new_cmd));
+      new_cmd->cmd = clone_agent_expr (current_cmd->cmd);
+      new_cmd->persistence = current_cmd->persistence;
+      APPEND_TO_LIST (&dest->command_list, new_cmd, cmd_tail);
+    }
+
+  return dest;
+}
+
+/* Create a new breakpoint list NEW_LIST that is a copy of the
+   list starting at SRC_LIST.  Create the corresponding new
+   raw_breakpoint list NEW_RAW_LIST as well.  */
+
+void
+clone_all_breakpoints (struct breakpoint **new_list,
+		       struct raw_breakpoint **new_raw_list,
+		       const struct breakpoint *src_list)
+{
+  const struct breakpoint *bp;
+  struct breakpoint *new_bkpt;
+  struct breakpoint *bkpt_tail = NULL;
+  struct raw_breakpoint *raw_bkpt_tail = NULL;
+
+  for (bp = src_list; bp != NULL; bp = bp->next)
+    {
+      new_bkpt = clone_one_breakpoint (bp);
+      APPEND_TO_LIST (new_list, new_bkpt, bkpt_tail);
+      APPEND_TO_LIST (new_raw_list, new_bkpt->raw, raw_bkpt_tail);
+    }
+}
diff --git a/gdb/gdbserver/mem-break.h b/gdb/gdbserver/mem-break.h
index 8b010c1..b5a3208 100644
--- a/gdb/gdbserver/mem-break.h
+++ b/gdb/gdbserver/mem-break.h
@@ -253,4 +253,10 @@ int insert_memory_breakpoint (struct raw_breakpoint *bp);
 
 int remove_memory_breakpoint (struct raw_breakpoint *bp);
 
+/* Create a new breakpoint list NEW_BKPT_LIST that is a copy of SRC.  */
+
+void clone_all_breakpoints (struct breakpoint **new_bkpt_list,
+			    struct raw_breakpoint **new_raw_bkpt_list,
+			    const struct breakpoint *src);
+
 #endif /* MEM_BREAK_H */
-- 
1.8.1.1

^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH v5 05/06] Remote follow vfork
  2015-02-27  0:46   ` [PATCH v5 0/6] " Don Breazeal
                       ` (3 preceding siblings ...)
  2015-02-27  0:47     ` [PATCH v5 03/06] Extended-remote Linux follow fork Don Breazeal
@ 2015-02-27  0:47     ` Don Breazeal
  2015-02-27  0:48     ` [PATCH v5 06/06] Remote fork catch Don Breazeal
                       ` (3 subsequent siblings)
  8 siblings, 0 replies; 30+ messages in thread
From: Don Breazeal @ 2015-02-27  0:47 UTC (permalink / raw)
  To: gdb-patches, palves

This is an updated version of the patch last reviewed here:
https://sourceware.org/ml/gdb-patches/2015-02/msg00263.html.

The patch has been modified to remove all 'target remote' support,
which will be added in a later patch.

Hi Pedro, here are responses to your review comments:

On 2/10/2015 8:39 AM, Pedro Alves wrote:
>On 01/25/2015 09:46 PM, Don Breazeal wrote:
>> --- a/gdb/remote.c
>> +++ b/gdb/remote.c
>> @@ -1481,7 +1481,10 @@ remote_follow_fork (struct target_ops *target, int follow_child,
>>  	  pid_t child_pid;
>>  
>>  	  gdb_assert ((inferior_thread ()->pending_follow.kind
>> -		       == TARGET_WAITKIND_FORKED));
>> +		       == TARGET_WAITKIND_FORKED)
>> +		      || (inferior_thread ()->pending_follow.kind
>> +			  == TARGET_WAITKIND_VFORKED));
>> +
>
> Please use a temporary variable, something like:
> 
>           kind = inferior_thread ()->pending_follow.kind
>  	  gdb_assert (kind == TARGET_WAITKIND_FORKED)
> 		      || (kind == TARGET_WAITKIND_VFORKED));
> 
> Makes it easier to debug a core dump caused by this assert.

Done.

> 
>>  	  child_ptid = inferior_thread ()->pending_follow.value.related_pid;
>>  	  child_pid = ptid_get_pid (child_ptid);
>>  
>> @@ -4502,7 +4505,8 @@ remote_detach_1 (struct target_ops *ops, const char *args,
>>      {
>>        inferior_ptid = null_ptid;
>>        detach_inferior (pid);
>> -      inf_child_maybe_unpush_target (ops);
>> +      if (!extended)
>> +	inf_child_maybe_unpush_target (ops);
>
> Hmm, I'm afraid I don't understand why is this part of this patch?

It was another manifestation of the "unpush" confusion I had in some of
the previous patches, and an artifact of the 'target remote' work.  I
have removed this.

The updated patch is below.

Thanks
--Don

This patch implements follow-fork for vfork on remote and extended-remote
Linux targets.

The implementation follows the native implementation as much as possible.
Most of the work is done on the GDB side in the existing code now in
infrun.c.  GDBserver just has to report the events and do a little
bookkeeping.

Implementation was almost entirely in gdbserver, excepting changes to
gdb/remote.c, and included:

 * enabling VFORK events by adding ptrace options for VFORK and VFORK_DONE
   as 'additional options' in linux-low.c:initialize_low.

 * handling VFORK and VFORK_DONE events in linux-low.c:handle_extended_wait
   and reporting them to GDB.
 
 * including VFORK and VFORK_DONE events in the predicate
   linux-low.c:extended_event_reported.

 * adding support for VFORK and VFORK_DONE events in RSP by adding stop
   reasons "vfork" and "vforkdone" to the 'T' Stop Reply Packet in both
   gdbserver/remote-utils.c and gdb/remote.c.

Tested on x64 Ubuntu Lucid, native, remote, extended-remote.

Thanks
--Don

gdb/doc/
2015-02-25  Don Breazeal  <donb@codesourcery.com>

	* gdb.texinfo (Stop Reply Packets): List new stop reasons
	"vfork" and "vforkdone".

gdb/gdbserver/
2015-02-25  Don Breazeal  <donb@codesourcery.com>

	* linux-low.c (handle_extended_wait): Handle PTRACE_EVENT_FORK and
	PTRACE_EVENT_VFORK_DONE.
	(linux_low_enable_events): Prevent enablement of VFORK events if
	GDB has not requested them.
	(extended_event_reported): Add vfork and vfork-done to the list
	of extended events.
	(initialize_low): Enable PTRACE_EVENT_FORK and PTRACE_EVENT_VFORK_DONE.
	* remote-utils.c (prepare_resume_reply): New stop reasons "vfork"
	and "vforkdone" for RSP 'T' Stop Reply Packet.
	* server.h (report_vfork_events): Declare
	global variable.

gdb/
2015-02-25  Don Breazeal  <donb@codesourcery.com>

	* NEWS: Add vfork and vforkdone to the announcements about fork
	event support and RSP support for fork events.
	* remote.c (remote_follow_fork): Add vfork event type to assert.
	(remote_parse_stop_reply): New stop reasons "vfork" and
	"vforkdone" for RSP 'T' Stop Reply Packet.

---
 gdb/NEWS                     | 11 +++++++----
 gdb/doc/gdb.texinfo          | 14 ++++++++++++++
 gdb/gdbserver/linux-low.c    | 30 +++++++++++++++++++++++++-----
 gdb/gdbserver/remote-utils.c | 17 +++++++++++++++--
 gdb/gdbserver/server.h       |  1 +
 gdb/remote.c                 | 26 ++++++++++++++++++++++++--
 6 files changed, 86 insertions(+), 13 deletions(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index 00cca93..db62e35 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -64,8 +64,11 @@ Qbtrace-conf:bts:size
   Set the requested ring buffer size for branch tracing in BTS format.
 
 T Stop Reply Packet's reason
-  The T stop reply packet supports a new stop reason 'xfork', which
-  signifies that the specified inferior has executed a fork.
+  The T stop reply packet supports new stop reasons 'xfork', 'vfork',
+  and 'vforkdone'.  The 'xfork' and 'vfork' reasons signify that the
+  specified inferior has executed a fork or vfork, respectively. The
+  'vforkdone' reason signifies that the a vforked child process has
+  executed either an exec or exit.
 
 * The info record command now shows the recording format and the
   branch tracing configuration for the current thread when using
@@ -81,8 +84,8 @@ T Stop Reply Packet's reason
   GDB's qSupported query.
 
   GDBserver extended-remote Linux targets now provides basic support
-  for fork events.  This enables follow-fork-mode and detach-on-fork
-  for those targets with Linux kernels 2.5.60 and later.
+  for fork and vfork events.  This enables follow-fork-mode and
+  detach-on-fork for those targets with Linux kernels 2.5.60 and later.
 
 *** Changes in GDB 7.9
 
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 2276976..e23b49c 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -35190,6 +35190,20 @@ The packet indicates that @code{fork} was called, and @var{r}
 is the ptid of the new child process.  This packet is only
 applicable to targets that support fork events.
 
+@cindex vfork events, remote reply
+@item vfork
+The packet indicates that @code{vfork} was called, and @var{r}
+is the ptid of the new child process.  This packet is only
+applicable to targets that support vfork events.
+
+@cindex vforkdone events, remote reply
+@item vforkdone
+The packet indicates that a child process created by a vfork
+has either called @code{exec} or terminated, so that the
+address spaces of the parent and child process are no longer
+shared. The @var{r} part is ignored.  This packet is only
+applicable to targets that support vforkdone events.
+
 @cindex replay log events, remote reply
 @item replaylog
 The packet indicates that the target cannot continue replaying 
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 9a50353..5f9f9d4 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -382,7 +382,8 @@ handle_extended_wait (struct lwp_info *event_child, int wstat)
   struct thread_info *event_thr = get_lwp_thread (event_child);
   struct lwp_info *new_lwp;
 
-  if ((event == PTRACE_EVENT_FORK) || (event == PTRACE_EVENT_CLONE))
+  if ((event == PTRACE_EVENT_FORK) || (event == PTRACE_EVENT_VFORK)
+      || (event == PTRACE_EVENT_CLONE))
     {
       ptid_t ptid;
       unsigned long new_pid;
@@ -408,7 +409,7 @@ handle_extended_wait (struct lwp_info *event_child, int wstat)
 	    warning ("wait returned unexpected status 0x%x", status);
 	}
 
-      if (event == PTRACE_EVENT_FORK)
+      if (event == PTRACE_EVENT_FORK || event == PTRACE_EVENT_VFORK)
 	{
 	  struct process_info *parent_proc;
 	  struct process_info *child_proc;
@@ -453,8 +454,13 @@ handle_extended_wait (struct lwp_info *event_child, int wstat)
 
 	  /* Save fork info in the parent thread.  */
 	  parent_lwp = get_thread_lwp (event_thr);
-	  parent_lwp->waitstatus.kind = TARGET_WAITKIND_FORKED;
+	  if (event == PTRACE_EVENT_FORK)
+	    parent_lwp->waitstatus.kind = TARGET_WAITKIND_FORKED;
+	  else if (event == PTRACE_EVENT_VFORK)
+	    parent_lwp->waitstatus.kind = TARGET_WAITKIND_VFORKED;
+
 	  parent_lwp->waitstatus.value.related_pid = ptid;
+
 	  /* The status_pending field contains bits denoting the
 	     extended event, so when the pending event is handled,
 	     the handler will look at lwp->waitstatus.  */
@@ -497,6 +503,14 @@ handle_extended_wait (struct lwp_info *event_child, int wstat)
       /* Don't report the event.  */
       return 1;
     }
+  else if (event == PTRACE_EVENT_VFORK_DONE)
+    {
+      struct lwp_info *parent_lwp;
+
+      parent_lwp = get_thread_lwp (event_thr);
+      parent_lwp->waitstatus.kind = TARGET_WAITKIND_VFORK_DONE;
+      return 0;
+    }
 
   internal_error (__FILE__, __LINE__, _("unknown ptrace event %d"), event);
 }
@@ -1846,6 +1860,8 @@ linux_low_enable_events (pid_t pid, int attached)
 {
   if (!report_fork_events)
     linux_ptrace_clear_flags (PTRACE_O_TRACEFORK);
+  if (!report_vfork_events)
+    linux_ptrace_clear_flags (PTRACE_O_TRACEVFORK);
 
   linux_enable_event_reporting (pid, attached);
 }
@@ -2528,7 +2544,9 @@ extended_event_reported (const struct target_waitstatus *waitstatus)
   if (waitstatus == NULL)
     return 0;
 
-  return (waitstatus->kind == TARGET_WAITKIND_FORKED);
+  return (waitstatus->kind == TARGET_WAITKIND_FORKED
+	  || waitstatus->kind == TARGET_WAITKIND_VFORKED
+	  || waitstatus->kind == TARGET_WAITKIND_VFORK_DONE);
 }
 
 /* Wait for process, returns status.  */
@@ -6355,6 +6373,8 @@ initialize_low (void)
   initialize_low_arch ();
 
   /* Enable extended ptrace events.  */
-  linux_ptrace_set_additional_flags (PTRACE_O_TRACEFORK);
+  linux_ptrace_set_additional_flags (PTRACE_O_TRACEFORK
+					       | PTRACE_O_TRACEVFORK
+					       | PTRACE_O_TRACEVFORKDONE);
   linux_check_ptrace_features ();
 }
diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c
index 6191e58..56f4246 100644
--- a/gdb/gdbserver/remote-utils.c
+++ b/gdb/gdbserver/remote-utils.c
@@ -1115,15 +1115,19 @@ prepare_resume_reply (char *buf, ptid_t ptid,
     {
     case TARGET_WAITKIND_STOPPED:
     case TARGET_WAITKIND_FORKED:
+    case TARGET_WAITKIND_VFORKED:
       {
 	struct thread_info *saved_thread;
 	const char **regp;
 	struct regcache *regcache;
 
-	if (status->kind == TARGET_WAITKIND_FORKED && report_fork_events)
+	if ((status->kind == TARGET_WAITKIND_FORKED && report_fork_events)
+	     || (status->kind == TARGET_WAITKIND_VFORKED
+		 && report_vfork_events))
 	  {
 	    enum gdb_signal signal = GDB_SIGNAL_TRAP;
-	    const char *event = "xfork";
+	    const char *event = (status->kind == TARGET_WAITKIND_FORKED
+				 ? "xfork" : "vfork");
 
 	    sprintf (buf, "T%02x%s:", signal, event);
 	    buf += strlen (buf);
@@ -1235,6 +1239,15 @@ prepare_resume_reply (char *buf, ptid_t ptid,
       else
 	sprintf (buf, "X%02x", status->value.sig);
       break;
+    case TARGET_WAITKIND_VFORK_DONE:
+      if (multi_process)
+	{
+	  enum gdb_signal signal = GDB_SIGNAL_TRAP;
+	  const char *event = "vforkdone";
+
+	  sprintf (buf, "T%02x%s:;", signal, event);
+	}
+      break;
     default:
       error ("unhandled waitkind");
       break;
diff --git a/gdb/gdbserver/server.h b/gdb/gdbserver/server.h
index ce6ffb9..5284dac 100644
--- a/gdb/gdbserver/server.h
+++ b/gdb/gdbserver/server.h
@@ -85,6 +85,7 @@ extern int disable_packet_qfThreadInfo;
 extern int run_once;
 extern int multi_process;
 extern int report_fork_events;
+extern int report_vfork_events;
 extern int non_stop;
 
 extern int disable_randomization;
diff --git a/gdb/remote.c b/gdb/remote.c
index 3a6c6cb..13fd837 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -4496,15 +4496,20 @@ remote_follow_fork (struct target_ops *ops, int follow_child,
 {
   struct remote_state *rs = get_remote_state ();
 
+  /* If fork events aren't supported, then return.  We know that if
+     fork events are supported, then so are vfork events, since they
+     were both introduced in the same version of the linux kernel.  */
   if (remote_fork_event_p (rs))
     {
       if (detach_fork && !follow_child)
 	{
 	  ptid_t parent_ptid;
 	  ptid_t child_ptid;
+	  enum target_waitkind kind;
 
-	  gdb_assert (inferior_thread ()->pending_follow.kind
-		      == TARGET_WAITKIND_FORKED);
+	  kind = inferior_thread ()->pending_follow.kind;
+	  gdb_assert (kind == TARGET_WAITKIND_FORKED
+		      || kind == TARGET_WAITKIND_VFORKED);
 
 	  /* remote_detach_1 detaches inferior_ptid, which is currently
 	     the ptid of the parent.  Switch inferior_ptid to the ptid
@@ -5667,6 +5672,23 @@ Packet: '%s'\n"),
 	      event->ws.value.related_pid = read_ptid (++p1, &p);
 	      event->ws.kind = TARGET_WAITKIND_FORKED;
 	    }
+	  else if (strncmp (p, "vfork", p1 - p) == 0)
+	    {
+	      event->ws.value.related_pid = read_ptid (++p1, &p);
+	      event->ws.kind = TARGET_WAITKIND_VFORKED;
+	    }
+	  else if (strncmp (p, "vforkdone", p1 - p) == 0)
+	    {
+	      char *p_temp;
+
+	      p1++;
+	      p_temp = p1;
+	      while (*p_temp && *p_temp != ';')
+		p_temp++;
+
+	      event->ws.kind = TARGET_WAITKIND_VFORK_DONE;
+	      p = p_temp;
+	    }
 	  else
 	    {
 	      ULONGEST pnum;
-- 
1.8.1.1

^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH v5 04/06] Arch-specific remote follow fork
  2015-02-27  0:46   ` [PATCH v5 0/6] " Don Breazeal
  2015-02-27  0:46     ` [PATCH v5 01/06] Identify remote fork event support Don Breazeal
@ 2015-02-27  0:47     ` Don Breazeal
  2015-02-27  7:37       ` Eli Zaretskii
  2015-02-27  0:47     ` [PATCH v5 02/06] Clone remote breakpoints Don Breazeal
                       ` (6 subsequent siblings)
  8 siblings, 1 reply; 30+ messages in thread
From: Don Breazeal @ 2015-02-27  0:47 UTC (permalink / raw)
  To: gdb-patches, palves

This version of this patch is unchanged except possibly for merges from
the mainline and adding a NEWS change.  It has also been moved forward in
the patch series.  It was previously approved here:
https://sourceware.org/ml/gdb-patches/2015-02/msg00262.html

Thanks,
--Don


This patch implements the architecture-specific pieces of follow-fork
for remote and extended-remote Linux targets, which in the current
implementation copyies the parent's debug register state into the new
child's data structures.  This is required for x86, arm, aarch64, and
mips.

This follows the native implementation as closely as possible by
implementing a new linux_target_ops function 'new_fork', which is
analogous to 'linux_nat_new_fork' in linux-nat.c.  In gdbserver, the debug
registers are stored in the process list, instead of an
architecture-specific list, so the function arguments are process_info
pointers instead of an lwp_info and a pid as in the native implementation.

In the MIPS implementation the debug register mirror is stored differently
from x86, ARM, and aarch64, so instead of doing a simple structure assignment
I had to clone the list of watchpoint structures.

Tested using gdb.threads/watchpoint-fork.exp on x86, and ran manual tests
on a MIPS board and an ARM board.

I don't currently have access to an aarch64 board, so if someone is able
to test this easily, please do.

gdb/
2015-02-25  Don Breazeal  <donb@codesourcery.com>

	* NEWS: Remove hardware breakpoint caveat from announcement
	of remote follow fork support.

gdb/gdbserver/
2015-02-25  Don Breazeal  <donb@codesourcery.com>

	* linux-aarch64-low.c (aarch64_linux_new_fork): New function.
	(the_low_target) <new_fork>: Initialize new member.
	* linux-arm-low.c (arm_new_fork): New function.
	(the_low_target) <new_fork>: Initialize new member.
	* linux-low.c (handle_extended_wait): Call new target function
	new_fork.
	* linux-low.h (struct linux_target_ops) <new_fork>: New member.
	* linux-mips-low.c (mips_add_watchpoint): New function
	extracted from mips_insert_point.
	(the_low_target) <new_fork>: Initialize new member.
	(mips_linux_new_fork): New function.
	(mips_insert_point): Call mips_add_watchpoint.
	* linux-x86-low.c (x86_linux_new_fork): New function.
	(the_low_target) <new_fork>: Initialize new member.


---
 gdb/NEWS                          |  3 +-
 gdb/gdbserver/linux-aarch64-low.c | 28 +++++++++++++++
 gdb/gdbserver/linux-arm-low.c     | 42 ++++++++++++++++++++++
 gdb/gdbserver/linux-low.c         |  4 +++
 gdb/gdbserver/linux-low.h         |  3 ++
 gdb/gdbserver/linux-mips-low.c    | 76 ++++++++++++++++++++++++++++++++-------
 gdb/gdbserver/linux-x86-low.c     | 29 +++++++++++++++
 7 files changed, 171 insertions(+), 14 deletions(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index bd7fac9..00cca93 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -82,8 +82,7 @@ T Stop Reply Packet's reason
 
   GDBserver extended-remote Linux targets now provides basic support
   for fork events.  This enables follow-fork-mode and detach-on-fork
-  for those targets with Linux kernels 2.5.60 and later.  Hardware
-  watchpoints are not inherited across a fork in this implementation.
+  for those targets with Linux kernels 2.5.60 and later.
 
 *** Changes in GDB 7.9
 
diff --git a/gdb/gdbserver/linux-aarch64-low.c b/gdb/gdbserver/linux-aarch64-low.c
index 6b84042..f0cd339 100644
--- a/gdb/gdbserver/linux-aarch64-low.c
+++ b/gdb/gdbserver/linux-aarch64-low.c
@@ -1135,6 +1135,33 @@ aarch64_linux_new_thread (void)
   return info;
 }
 
+static void
+aarch64_linux_new_fork (struct process_info *parent,
+			struct process_info *child)
+{
+  /* These are allocated by linux_add_process.  */
+  gdb_assert (parent->private != NULL
+	      && parent->private->arch_private != NULL);
+  gdb_assert (child->private != NULL
+	      && child->private->arch_private != NULL);
+
+  /* Linux kernel before 2.6.33 commit
+     72f674d203cd230426437cdcf7dd6f681dad8b0d
+     will inherit hardware debug registers from parent
+     on fork/vfork/clone.  Newer Linux kernels create such tasks with
+     zeroed debug registers.
+
+     GDB core assumes the child inherits the watchpoints/hw
+     breakpoints of the parent, and will remove them all from the
+     forked off process.  Copy the debug registers mirrors into the
+     new process so that all breakpoints and watchpoints can be
+     removed together.  The debug registers mirror will become zeroed
+     in the end before detaching the forked off process, thus making
+     this compatible with older Linux kernels too.  */
+
+  *child->private->arch_private = *parent->private->arch_private;
+}
+
 /* Called when resuming a thread.
    If the debug regs have changed, update the thread's copies.  */
 
@@ -1299,6 +1326,7 @@ struct linux_target_ops the_low_target =
   NULL,
   aarch64_linux_new_process,
   aarch64_linux_new_thread,
+  aarch64_linux_new_fork,
   aarch64_linux_prepare_to_resume,
 };
 
diff --git a/gdb/gdbserver/linux-arm-low.c b/gdb/gdbserver/linux-arm-low.c
index 303d9c8..97f3848 100644
--- a/gdb/gdbserver/linux-arm-low.c
+++ b/gdb/gdbserver/linux-arm-low.c
@@ -717,6 +717,47 @@ arm_new_thread (void)
   return info;
 }
 
+static void
+arm_new_fork (struct process_info *parent, struct process_info *child)
+{
+  struct arch_process_info *parent_proc_info = parent->private->arch_private;
+  struct arch_process_info *child_proc_info = child->private->arch_private;
+  struct lwp_info *child_lwp;
+  struct arch_lwp_info *child_lwp_info;
+  int i;
+
+  /* These are allocated by linux_add_process.  */
+  gdb_assert (parent->private != NULL
+	      && parent->private->arch_private != NULL);
+  gdb_assert (child->private != NULL
+	      && child->private->arch_private != NULL);
+
+  /* Linux kernel before 2.6.33 commit
+     72f674d203cd230426437cdcf7dd6f681dad8b0d
+     will inherit hardware debug registers from parent
+     on fork/vfork/clone.  Newer Linux kernels create such tasks with
+     zeroed debug registers.
+
+     GDB core assumes the child inherits the watchpoints/hw
+     breakpoints of the parent, and will remove them all from the
+     forked off process.  Copy the debug registers mirrors into the
+     new process so that all breakpoints and watchpoints can be
+     removed together.  The debug registers mirror will become zeroed
+     in the end before detaching the forked off process, thus making
+     this compatible with older Linux kernels too.  */
+
+  *child_proc_info = *parent_proc_info;
+
+  /* Mark all the hardware breakpoints and watchpoints as changed to
+     make sure that the registers will be updated.  */
+  child_lwp = find_lwp_pid (ptid_of (child));
+  child_lwp_info = child_lwp->arch_private;
+  for (i = 0; i < MAX_BPTS; i++)
+    child_lwp_info->bpts_changed[i] = 1;
+  for (i = 0; i < MAX_WPTS; i++)
+    child_lwp_info->wpts_changed[i] = 1;
+}
+
 /* Called when resuming a thread.
    If the debug regs have changed, update the thread's copies.  */
 static void
@@ -920,6 +961,7 @@ struct linux_target_ops the_low_target = {
   NULL, /* siginfo_fixup */
   arm_new_process,
   arm_new_thread,
+  arm_new_fork,
   arm_prepare_to_resume,
 };
 
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index d26282b..9a50353 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -447,6 +447,10 @@ handle_extended_wait (struct lwp_info *event_child, int wstat)
 	  child_proc->tdesc = tdesc;
 	  child_lwp->must_set_ptrace_flags = 1;
 
+	  /* Clone arch-specific process data.  */
+	  if (the_low_target.new_fork != NULL)
+	    the_low_target.new_fork (parent_proc, child_proc);
+
 	  /* Save fork info in the parent thread.  */
 	  parent_lwp = get_thread_lwp (event_thr);
 	  parent_lwp->waitstatus.kind = TARGET_WAITKIND_FORKED;
diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h
index 0a53eaf..0263e97 100644
--- a/gdb/gdbserver/linux-low.h
+++ b/gdb/gdbserver/linux-low.h
@@ -185,6 +185,9 @@ struct linux_target_ops
      allocate it here.  */
   struct arch_lwp_info * (*new_thread) (void);
 
+  /* Hook to call, if any, when a new fork is attached.  */
+  void (*new_fork) (struct process_info *parent, struct process_info *child);
+
   /* Hook to call prior to resuming a thread.  */
   void (*prepare_to_resume) (struct lwp_info *);
 
diff --git a/gdb/gdbserver/linux-mips-low.c b/gdb/gdbserver/linux-mips-low.c
index 478bb6e..500cf3e 100644
--- a/gdb/gdbserver/linux-mips-low.c
+++ b/gdb/gdbserver/linux-mips-low.c
@@ -344,6 +344,68 @@ mips_linux_new_thread (void)
   return info;
 }
 
+/* Create a new mips_watchpoint and add it to the list.  */
+
+static void
+mips_add_watchpoint (struct arch_process_info *private, CORE_ADDR addr,
+		     int len, int watch_type)
+{
+  struct mips_watchpoint *new_watch;
+  struct mips_watchpoint **pw;
+
+  new_watch = xmalloc (sizeof (struct mips_watchpoint));
+  new_watch->addr = addr;
+  new_watch->len = len;
+  new_watch->type = watch_type;
+  new_watch->next = NULL;
+
+  pw = &private->current_watches;
+  while (*pw != NULL)
+    pw = &(*pw)->next;
+  *pw = new_watch;
+}
+
+/* Hook to call when a new fork is attached.  */
+
+static void
+mips_linux_new_fork (struct process_info *parent,
+			struct process_info *child)
+{
+  struct arch_process_info *parent_private;
+  struct arch_process_info *child_private;
+  struct mips_watchpoint *wp;
+
+  /* These are allocated by linux_add_process.  */
+  gdb_assert (parent->private != NULL
+	      && parent->private->arch_private != NULL);
+  gdb_assert (child->private != NULL
+	      && child->private->arch_private != NULL);
+
+  /* Linux kernel before 2.6.33 commit
+     72f674d203cd230426437cdcf7dd6f681dad8b0d
+     will inherit hardware debug registers from parent
+     on fork/vfork/clone.  Newer Linux kernels create such tasks with
+     zeroed debug registers.
+
+     GDB core assumes the child inherits the watchpoints/hw
+     breakpoints of the parent, and will remove them all from the
+     forked off process.  Copy the debug registers mirrors into the
+     new process so that all breakpoints and watchpoints can be
+     removed together.  The debug registers mirror will become zeroed
+     in the end before detaching the forked off process, thus making
+     this compatible with older Linux kernels too.  */
+
+  parent_private = parent->private->arch_private;
+  child_private = child->private->arch_private;
+
+  child_private->watch_readback_valid = parent_private->watch_readback_valid;
+  child_private->watch_readback = parent_private->watch_readback;
+
+  for (wp = parent_private->current_watches; wp != NULL; wp = wp->next)
+    mips_add_watchpoint (child_private, wp->addr, wp->len, wp->type);
+
+  child_private->watch_mirror = parent_private->watch_mirror;
+}
 /* This is the implementation of linux_target_ops method
    prepare_to_resume.  If the watch regs have changed, update the
    thread's copies.  */
@@ -397,8 +459,6 @@ mips_insert_point (enum raw_bkpt_type type, CORE_ADDR addr,
   struct process_info *proc = current_process ();
   struct arch_process_info *private = proc->private->arch_private;
   struct pt_watch_regs regs;
-  struct mips_watchpoint *new_watch;
-  struct mips_watchpoint **pw;
   int pid;
   long lwpid;
   enum target_hw_bp_type watch_type;
@@ -425,16 +485,7 @@ mips_insert_point (enum raw_bkpt_type type, CORE_ADDR addr,
     return -1;
 
   /* It fit.  Stick it on the end of the list.  */
-  new_watch = xmalloc (sizeof (struct mips_watchpoint));
-  new_watch->addr = addr;
-  new_watch->len = len;
-  new_watch->type = watch_type;
-  new_watch->next = NULL;
-
-  pw = &private->current_watches;
-  while (*pw != NULL)
-    pw = &(*pw)->next;
-  *pw = new_watch;
+  mips_add_watchpoint (private, addr, len, watch_type);
 
   private->watch_mirror = regs;
 
@@ -845,6 +896,7 @@ struct linux_target_ops the_low_target = {
   NULL, /* siginfo_fixup */
   mips_linux_new_process,
   mips_linux_new_thread,
+  mips_linux_new_fork,
   mips_linux_prepare_to_resume
 };
 
diff --git a/gdb/gdbserver/linux-x86-low.c b/gdb/gdbserver/linux-x86-low.c
index e58a7ac..cd931f4 100644
--- a/gdb/gdbserver/linux-x86-low.c
+++ b/gdb/gdbserver/linux-x86-low.c
@@ -771,6 +771,34 @@ x86_linux_new_thread (void)
   return info;
 }
 
+/* Target routine for linux_new_fork.  */
+
+static void
+x86_linux_new_fork (struct process_info *parent, struct process_info *child)
+{
+  /* These are allocated by linux_add_process.  */
+  gdb_assert (parent->private != NULL
+	      && parent->private->arch_private != NULL);
+  gdb_assert (child->private != NULL
+	      && child->private->arch_private != NULL);
+
+  /* Linux kernel before 2.6.33 commit
+     72f674d203cd230426437cdcf7dd6f681dad8b0d
+     will inherit hardware debug registers from parent
+     on fork/vfork/clone.  Newer Linux kernels create such tasks with
+     zeroed debug registers.
+
+     GDB core assumes the child inherits the watchpoints/hw
+     breakpoints of the parent, and will remove them all from the
+     forked off process.  Copy the debug registers mirrors into the
+     new process so that all breakpoints and watchpoints can be
+     removed together.  The debug registers mirror will become zeroed
+     in the end before detaching the forked off process, thus making
+     this compatible with older Linux kernels too.  */
+
+  *child->private->arch_private = *parent->private->arch_private;
+}
+
 /* Called when resuming a thread.
    If the debug regs have changed, update the thread's copies.  */
 
@@ -3431,6 +3459,7 @@ struct linux_target_ops the_low_target =
   x86_siginfo_fixup,
   x86_linux_new_process,
   x86_linux_new_thread,
+  x86_linux_new_fork,
   x86_linux_prepare_to_resume,
   x86_linux_process_qsupported,
   x86_supports_tracepoints,
-- 
1.8.1.1

^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH v5 06/06] Remote fork catch
  2015-02-27  0:46   ` [PATCH v5 0/6] " Don Breazeal
                       ` (4 preceding siblings ...)
  2015-02-27  0:47     ` [PATCH v5 05/06] Remote follow vfork Don Breazeal
@ 2015-02-27  0:48     ` Don Breazeal
  2015-03-02 18:29     ` [PATCH v5 0/6] Remote fork events Pedro Alves
                       ` (2 subsequent siblings)
  8 siblings, 0 replies; 30+ messages in thread
From: Don Breazeal @ 2015-02-27  0:48 UTC (permalink / raw)
  To: gdb-patches, palves

This patch implements catchpoints for fork events on extended-remote
Linux targets.

Implementation appeared to be straightforward, requiring four new functions
in remote.c to implement insert/remove of fork/vfork catchpoints.  These
functions are essentially stubs that just return 0 ('success') if the
required features are enabled.  If the fork events are being reported, then
catchpoints are set and hit.

However, there are some extra issues that arise with catchpoints.

1) Thread creation reporting -- fork catchpoints are hit before the
   follow_fork has been completed.  When stopped at a fork catchpoint
   in the native implementation, the new process is not 'reported'
   until after the follow is done.  It doesn't show up in the inferiors
   list or the threads list.  However, in the gdbserver case, an
   'info threads' while stopped at a fork catchpoint will retrieve the
   new thread info from the target and add it to GDB's data structures,
   prior to the follow operations.  Because of this premature report,
   things on the GDB side eventually get very confused.

   So in gdbserver, in server.c:handle_qxfer_threads_worker, we check
   'last_status' and if it shows a FORK event, we know that we are in an
   unfollowed fork and we do not report the new (forked) thread to GDB.

2) Kill process before fork is followed -- on the native side in
   linux-nat.c:linux_nat_kill, there is some code to handle the case where
   a fork has occurred but follow_fork hasn't been called yet.  It does
   this by using the last status to determine if a follow is pending, and
   if it is, to kill the child task.  This patch uses similar code in
   linux-low.c:linux_kill.

3) One of the tests related to fork catchpoints,
   gdb.threads/fork-thread-pending.exp, depended on the threads being
   reported in a specific order.  GDBserver reported the threads in a
   different order, that is, 'info threads' showed the same threads, but in
   a different order.  The test used a hard-coded thread number to find a
   threads that (a) was not the main thread (blocked in pthread_join), and
   (b) was not the forking thread (stopped in fork).

   I implemented a new proc, find_unforked_thread, that uses a pretty
   brute-force method of finding a thread.  I considered just hard-coding
   another number (the native case used thread 2, which was the forking
   thread in the remote case), but that didn't seem future-proof.
   Suggestions on how to do this better would be welcome.
  
Tested on x64 Ubuntu Lucid, native, remote, extended-remote.  Tested the
case of killing the forking process before the fork has been followed
manually.  It wasn't clear to me how to check that a process had actually
been killed from a dejagnu test.

Thanks
--Don

gdb/gdbserver/
2015-02-25  Don Breazeal  <donb@codesourcery.com>

	* linux-low.c (linux_kill): Kill forked child when between
	fork event and follow_fork.
	* server.c (handle_qxfer_threads_worker): Skip forked child thread
	when between fork and follow_fork.
	(get_last_target_status): New function.
	* server.h (get_last_target_status): Declare new function.

gdb/
2015-02-25  Don Breazeal  <donb@codesourcery.com>
	* remote.c (remote_insert_fork_catchpoint): New function.
	(remote_remove_fork_catchpoint): New function.
	(remote_insert_vfork_catchpoint): New function.
	(remote_remove_vfork_catchpoint): New function.
	(init_extended_remote_ops): Initialize target vector with
	new fork catchpoint functions.

gdb/testsuite/
2015-02-25  Don Breazeal  <donb@codesourcery.com>
	* gdb.threads/fork-thread-pending.exp (find_unforked_thread):
	New proc.
	(main program): Call find_unforked_thread.

---
 gdb/NEWS                                          |  7 +--
 gdb/doc/gdb.texinfo                               |  6 ++-
 gdb/gdbserver/linux-low.c                         | 18 ++++++++
 gdb/gdbserver/server.c                            | 18 ++++++++
 gdb/gdbserver/server.h                            |  2 +
 gdb/remote.c                                      | 56 +++++++++++++++++++++++
 gdb/testsuite/gdb.threads/fork-thread-pending.exp | 23 +++++++++-
 7 files changed, 124 insertions(+), 6 deletions(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index db62e35..eba4c73 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -83,9 +83,10 @@ T Stop Reply Packet's reason
   The remote stub now reports support for fork and vfork events to
   GDB's qSupported query.
 
-  GDBserver extended-remote Linux targets now provides basic support
-  for fork and vfork events.  This enables follow-fork-mode and
-  detach-on-fork for those targets with Linux kernels 2.5.60 and later.
+  GDBserver extended-remote Linux targets now provides support for
+  fork and vfork events.  This enables follow-fork-mode, detach-on-fork,
+  catch fork, and catch vfork for extended-remote targets with Linux
+  kernels 2.5.60 and later.
 
 *** Changes in GDB 7.9
 
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 4510ca7..f2d503a 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -4426,12 +4426,14 @@ Again, in this case @value{GDBN} would not be able to display syscall's names.
 @item fork
 @kindex catch fork
 A call to @code{fork}.  This is currently only available for HP-UX
-and @sc{gnu}/Linux.
+and @sc{gnu}/Linux, and when connected to @code{gdbserver} running
+on @sc{gnu}/Linux with @kbd{target extended-remote}.
 
 @item vfork
 @kindex catch vfork
 A call to @code{vfork}.  This is currently only available for HP-UX
-and @sc{gnu}/Linux.
+and @sc{gnu}/Linux, and when connected to @code{gdbserver} running
+on @sc{gnu}/Linux with @kbd{target extended-remote}.
 
 @item load @r{[}regexp@r{]}
 @itemx unload @r{[}regexp@r{]}
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 5f9f9d4..07512aa 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -1033,6 +1033,24 @@ linux_kill (int pid)
 {
   struct process_info *process;
   struct lwp_info *lwp;
+  struct target_waitstatus last;
+  ptid_t last_ptid;
+
+  /* If we're stopped while forking and we haven't followed yet,
+     kill the child task.  We need to do this first because the
+     parent will be sleeping if this is a vfork.  */
+
+  get_last_target_status (&last_ptid, &last);
+
+  if (last.kind == TARGET_WAITKIND_FORKED
+      || last.kind == TARGET_WAITKIND_VFORKED)
+    {
+      lwp = find_lwp_pid (last.value.related_pid);
+      gdb_assert (lwp != NULL);
+      kill_wait_lwp (lwp);
+      process = find_process_pid (ptid_get_pid (last.value.related_pid));
+      the_target->mourn (process);
+    }
 
   process = find_process_pid (pid);
   if (process == NULL)
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index 19e3ba4..bf5586e 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -1356,6 +1356,15 @@ handle_qxfer_threads_worker (struct inferior_list_entry *inf, void *arg)
   int core = target_core_of_thread (ptid);
   char core_s[21];
 
+  /* Skip new threads created as the result of a fork if we are not done
+     handling that fork event.  We won't know whether to tell GDB about
+     the new thread until we are done following the fork.  */
+  if ((last_status.kind == TARGET_WAITKIND_FORKED
+       || last_status.kind == TARGET_WAITKIND_VFORKED)
+      && (ptid_get_pid (last_status.value.related_pid)
+	  == ptid_get_pid (ptid)))
+    return;
+
   write_ptid (ptid_s, ptid);
 
   if (core != -1)
@@ -4124,3 +4133,12 @@ handle_target_event (int err, gdb_client_data client_data)
 
   return 0;
 }
+
+/* Retrieve the last waitstatus reported to GDB.  */
+
+void
+get_last_target_status (ptid_t *ptid, struct target_waitstatus *last)
+{
+  *ptid = last_ptid;
+  *last = last_status;
+}
diff --git a/gdb/gdbserver/server.h b/gdb/gdbserver/server.h
index 5284dac..86e584e 100644
--- a/gdb/gdbserver/server.h
+++ b/gdb/gdbserver/server.h
@@ -102,6 +102,8 @@ typedef int gdb_fildes_t;
 /* Functions from server.c.  */
 extern int handle_serial_event (int err, gdb_client_data client_data);
 extern int handle_target_event (int err, gdb_client_data client_data);
+extern void get_last_target_status (ptid_t *ptid,
+				    struct target_waitstatus *last);
 
 #include "remote-utils.h"
 
diff --git a/gdb/remote.c b/gdb/remote.c
index 13fd837..4c58699 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -1457,6 +1457,54 @@ remote_fork_event_p (struct remote_state *rs)
   return packet_support (PACKET_fork_event_feature) == PACKET_ENABLE;
 }
 
+/* Returns true if vfork events are supported.  */
+
+static int
+remote_vfork_event_p (struct remote_state *rs)
+{
+  return packet_support (PACKET_vfork_event_feature) == PACKET_ENABLE;
+}
+
+/* Insert fork catchpoint target routine.  If fork events are enabled
+   then return success, nothing more to do.  */
+
+static int
+remote_insert_fork_catchpoint (struct target_ops *ops, int pid)
+{
+  struct remote_state *rs = get_remote_state ();
+
+  return !remote_fork_event_p (rs);
+}
+
+/* Remove fork catchpoint target routine.  Nothing to do, just
+   return success.  */
+
+static int
+remote_remove_fork_catchpoint (struct target_ops *ops, int pid)
+{
+  return 0;
+}
+
+/* Insert vfork catchpoint target routine.  If vfork events are enabled
+   then return success, nothing more to do.  */
+
+static int
+remote_insert_vfork_catchpoint (struct target_ops *ops, int pid)
+{
+  struct remote_state *rs = get_remote_state ();
+
+  return !remote_vfork_event_p (rs);
+}
+
+/* Remove vfork catchpoint target routine.  Nothing to do, just
+   return success.  */
+
+static int
+remote_remove_vfork_catchpoint (struct target_ops *ops, int pid)
+{
+  return 0;
+}
+
 /* Tokens for use by the asynchronous signal handlers for SIGINT.  */
 static struct async_signal_handler *async_sigint_remote_twice_token;
 static struct async_signal_handler *async_sigint_remote_token;
@@ -11856,6 +11904,14 @@ Specify the serial device it is connected to (e.g. /dev/ttya).";
   extended_remote_ops.to_supports_disable_randomization
     = extended_remote_supports_disable_randomization;
   extended_remote_ops.to_follow_fork = remote_follow_fork;
+  extended_remote_ops.to_insert_fork_catchpoint
+    = remote_insert_fork_catchpoint;
+  extended_remote_ops.to_remove_fork_catchpoint
+    = remote_remove_fork_catchpoint;
+  extended_remote_ops.to_insert_vfork_catchpoint
+    = remote_insert_vfork_catchpoint;
+  extended_remote_ops.to_remove_vfork_catchpoint
+    = remote_remove_vfork_catchpoint;
 }
 
 static int
diff --git a/gdb/testsuite/gdb.threads/fork-thread-pending.exp b/gdb/testsuite/gdb.threads/fork-thread-pending.exp
index d229232..594f376 100644
--- a/gdb/testsuite/gdb.threads/fork-thread-pending.exp
+++ b/gdb/testsuite/gdb.threads/fork-thread-pending.exp
@@ -31,6 +31,26 @@ if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executab
     return -1
 }
 
+# Find a thread that did not fork and is not the main thread and
+# return its thread number.  We can't just hard-code the thread
+# number since we have no guarantee as to the ordering of the threads
+# in gdb.  We know that the main thread is in pthread_join and the
+# forking thread is in fork, so we use this rather ungainly regexp
+# to capture an entry from 'info threads' that doesn't show one of
+# those routines, then extract the thread number.
+
+proc find_unforked_thread { } {
+    gdb_test_multiple "info threads" "find unforked thread" {
+        -re "(\[^\r]*Thread\[^\r]* in \[^fp]\[^ot]\[^rh]\[^kr]\[^e]\[^a]\[^d]\[^_]\[^j]\[^\r]*\r\n)" {
+    	    regexp "(\[ 	]*)(\[0-9]*)(\[    ]*Thread\[^\r]*\r\n)" $expect_out(0,string) ignore lead_spc threadnum rest
+        }
+        timeout {
+	    set threadnum -1
+        }
+    }
+    return $threadnum
+}
+
 clean_restart ${binfile}
 
 if ![runto_main] then {
@@ -46,7 +66,8 @@ gdb_test "continue" "Catchpoint.*" "1, get to the fork event"
 
 gdb_test "info threads" " Thread .* Thread .* Thread .* Thread .*" "1, multiple threads found"
 
-gdb_test "thread 1" ".*" "1, switched away from event thread"
+set threadnum [find_unforked_thread]
+gdb_test "thread $threadnum" ".*" "1, switched away from event thread to thread $threadnum"
 
 gdb_test "continue" "Not resuming.*" "1, refused to resume"
 
-- 
1.8.1.1

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v5 04/06] Arch-specific remote follow fork
  2015-02-27  0:47     ` [PATCH v5 04/06] Arch-specific remote follow fork Don Breazeal
@ 2015-02-27  7:37       ` Eli Zaretskii
  0 siblings, 0 replies; 30+ messages in thread
From: Eli Zaretskii @ 2015-02-27  7:37 UTC (permalink / raw)
  To: Don Breazeal; +Cc: gdb-patches, palves

> From: Don Breazeal <donb@codesourcery.com>
> Date: Thu, 26 Feb 2015 16:46:15 -0800
> 
> This version of this patch is unchanged except possibly for merges from
> the mainline and adding a NEWS change.  It has also been moved forward in
> the patch series.  It was previously approved here:
> https://sourceware.org/ml/gdb-patches/2015-02/msg00262.html

The NEWS part is OK, thanks.

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v5 0/6] Remote fork events
  2015-02-27  0:46   ` [PATCH v5 0/6] " Don Breazeal
                       ` (5 preceding siblings ...)
  2015-02-27  0:48     ` [PATCH v5 06/06] Remote fork catch Don Breazeal
@ 2015-03-02 18:29     ` Pedro Alves
  2015-03-02 20:18     ` [PATCH] Tighten gdb.base/disp-step-syscall.exp (was: Re: [PATCH v5 0/6] Remote fork events) Pedro Alves
  2015-03-17 20:56     ` [PATCH v6 0/6] Remote fork events Don Breazeal
  8 siblings, 0 replies; 30+ messages in thread
From: Pedro Alves @ 2015-03-02 18:29 UTC (permalink / raw)
  To: Don Breazeal, gdb-patches

Thanks Don,

On a quick skim, this version is looking much better.

I'll apply this to my sandbox and take a deeper look.

Thanks,
Pedro Alves

On 02/27/2015 12:46 AM, Don Breazeal wrote:
> This is an update to the patch series implementing remote follow-fork.
> This update only implements fork and vfork events for extended remote,
> omitting exec events and 'target remote' for now.
> 
> Changes from the previous version include:
>  * Removed 'target remote' support, which is deferred for later.
>  * Fixes per review comments, notably
>    - issues with unpushing the target after detach or kill
>    - reorganizing/renaming some things, both to match the
>      native implementation better and for just plain better
>      organization.
>    - fixed and/or improved some of the event handling code.
>  * Also added documentation to each individual patch, rather than
>    saving it all for a subsequent 'documentation patch'.
> 
> The new series includes six patches as follows:
> 
> 1/6: Preparatory patch that implements qSupported support for fork events
>      and associated mechanisms.
> 
> 2/6: Implements functions to clone breakpoint lists in gdbserver.
> 
> 3/6: Implements follow fork for 'fork' but not 'vfork', for
>      extended-remote targets only.
> 
> 4/6: Adds the architecture-specific pieces of follow-fork that allows
>      hardware watchpoints to be inherited by a forked child.
>      This patch was previously approved, although I have added a
>      trivial change to gdb/NEWS in this patch.
> 
> 5/6: Adds follow fork for 'vfork'.
> 
> 6/6: Adds catchpoints for 'fork' and 'vfork', along with support for
>      killing a process that has forked but has not been followed.
> 
> TESTING:
> Testing was mostly done using x86_64 Ubuntu, with the exception of the
> architecture-specific patch, #4.  There are a few anomalies that show
> up, but don't signify any problem.
> 
>  - Intermediate patches show failures due to the lack of features
>    implemented in subsequent patches, like missing hardware watchpoint
>    or catchpoint support.
> 
>  - Some vfork tests fail due to the lack of exec event support.
> 
>  - There are a couple of tests that show new failures that actually
>    fail in the current mainline.  Details of these are as follows:
> 
>    * when vfork events are enabled, gdb.base/disp-step-syscall.exp
>      shows PASS => FAIL in .sum diffs.  The test actually always
>      fails.  With native/master, we see
> 
>       stepi^M
>       FAIL: gdb.base/disp-step-syscall.exp: vfork: stepi vfork insn
> (timeout)
> 
>      With remote and extended-remote/master, we see a bogus PASS result:
>       stepi^M
>       [Inferior 1 (process 9399) exited normally]^M
>       (gdb) PASS: gdb.base/disp-step-syscall.exp: vfork: stepi vfork insn
> 
>     The criteria to pass that test are pretty lax:
>       gdb_test "stepi" ".*" "stepi $syscall insn"
> 
>   * Similarly, once vfork events are enabled, gdb.base/watch-vfork.exp
>     shows PASS => FAIL in .sum diffs.  This test also always fails.  With
>     native/master, we see this:
> 
>      continue^M
>      Continuing.^M
>      FAIL: gdb.base/watch-vfork.exp: Watchpoint triggers after vfork
> (sw) (timeout)
> 
>     With extended-remote/master, we see this:
>      continue
>      Continuing.
>      [Inferior 1 (process 18866) exited normally]
>      (gdb) FAIL: gdb.base/watch-vfork.exp: Watchpoint triggers after
> vfork (sw) (the program exited)
> 
>     But once vfork events are enabled, we see:
>      continue
>      Continuing.
>      Detaching from process 17405
>      FAIL: gdb.base/watch-vfork.exp: Watchpoint triggers after vfork
> (sw) (timeout)
>     which more-or-less matches the native/master behavior.
> 
> Thanks,
> --Don
> 

^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH] Tighten gdb.base/disp-step-syscall.exp (was: Re: [PATCH v5 0/6] Remote fork events)
  2015-02-27  0:46   ` [PATCH v5 0/6] " Don Breazeal
                       ` (6 preceding siblings ...)
  2015-03-02 18:29     ` [PATCH v5 0/6] Remote fork events Pedro Alves
@ 2015-03-02 20:18     ` Pedro Alves
  2015-03-03  6:20       ` [PATCH] Tighten gdb.base/disp-step-syscall.exp Breazeal, Don
  2015-03-17 21:18       ` Breazeal, Don
  2015-03-17 20:56     ` [PATCH v6 0/6] Remote fork events Don Breazeal
  8 siblings, 2 replies; 30+ messages in thread
From: Pedro Alves @ 2015-03-02 20:18 UTC (permalink / raw)
  To: Don Breazeal, gdb-patches

On 02/27/2015 12:46 AM, Don Breazeal wrote:
>  - There are a couple of tests that show new failures that actually
>    fail in the current mainline.  Details of these are as follows:
> 
>    * when vfork events are enabled, gdb.base/disp-step-syscall.exp
>      shows PASS => FAIL in .sum diffs.  The test actually always
>      fails.  With native/master, we see
> 
>       stepi^M
>       FAIL: gdb.base/disp-step-syscall.exp: vfork: stepi vfork insn
> (timeout)
> 

Hmm, I don't see that here.  I get a full pass on x86_64 Fedora 20.
Can you try "set debug infrun 1" / "set debug lin-lwp 1" / "set debug displaced 1"
to check if there's a gdb or kernel bug here?

>      With remote and extended-remote/master, we see a bogus PASS result:
>       stepi^M
>       [Inferior 1 (process 9399) exited normally]^M
>       (gdb) PASS: gdb.base/disp-step-syscall.exp: vfork: stepi vfork insn
> 
>     The criteria to pass that test are pretty lax:
>       gdb_test "stepi" ".*" "stepi $syscall insn"

Yeah.  I see several other problems.  Here's a patch to improve it.

Comments?

Unfortunately, with your full series applied, I get this:

 (gdb) PASS: gdb.base/disp-step-syscall.exp: vfork: get hexadecimal valueof "$pc"
 stepi
 Detaching from process 29944
 Killing process(es): 29942 29944
 /home/pedro/gdb/mygit/src/gdb/gdbserver/linux-low.c:998: A problem internal to GDBserver has been detected.
 kill_wait_lwp: Assertion `res > 0' failed.
 /home/pedro/gdb/mygit/src/gdb/thread.c:1182: internal-error: switch_to_thread: Assertion `inf != NULL' failed.
 A problem internal to GDB has been detected,
 further debugging may prove unreliable.
 Quit this debugging session? (y or n) FAIL: gdb.base/disp-step-syscall.exp: vfork: stepi vfork insn (GDB internal error)
 Resyncing due to internal error.
 n

Note, you'll need this one:

 https://sourceware.org/ml/gdb-patches/2015-03/msg00045.html

for that internal error to result in a quick bail...

----------
From 1f825812d3f17a2940065d0de38592700e7437bc Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Mon, 2 Mar 2015 20:16:23 +0000
Subject: [PATCH] Tighten gdb.base/disp-step-syscall.exp

This fixes several problems with this test.

E.g,. with --target_board=native-extended-gdbserver on
x86_64 Fedora 20, I get:

 Running /home/pedro/gdb/mygit/src/gdb/testsuite/gdb.base/disp-step-syscall.exp ...
 FAIL: gdb.base/disp-step-syscall.exp: vfork: get hexadecimal valueof "$pc" (timeout)
 FAIL: gdb.base/disp-step-syscall.exp: vfork: single step over vfork final pc
 FAIL: gdb.base/disp-step-syscall.exp: vfork: delete break vfork insn
 FAIL: gdb.base/disp-step-syscall.exp: vfork: continue to marker (vfork) (the program is no longer running)

And with --target=native-gdbserver, I get:

 Running /home/pedro/gdb/mygit/src/gdb/testsuite/gdb.base/disp-step-syscall.exp ...
 KPASS: gdb.base/disp-step-syscall.exp: vfork: single step over vfork (PRMS server/13796)
 FAIL: gdb.base/disp-step-syscall.exp: vfork: get hexadecimal valueof "$pc" (timeout)
 FAIL: gdb.base/disp-step-syscall.exp: vfork: single step over vfork final pc
 FAIL: gdb.base/disp-step-syscall.exp: vfork: delete break vfork insn
 FAIL: gdb.base/disp-step-syscall.exp: vfork: continue to marker (vfork) (the program is no longer running)

First, the lack of fork support on remote targets is supposed to be
kfailed, so the KPASS is obviously bogus.  The extended-remote board
should have KFAILed too.

The problem is that the test is using "is_remote" instead of
gdb_is_target_remote.

And then, I get:

 (gdb) PASS: gdb.base/disp-step-syscall.exp: vfork: set displaced-stepping on
 stepi

 Program terminated with signal SIGSEGV, Segmentation fault.
 The program no longer exists.
 (gdb) PASS: gdb.base/disp-step-syscall.exp: vfork: single step over vfork

Obviously, that should be a FAIL.  The problem is that the test only
expects SIGILL, not SIGSEGV.  It also doesn't bail correctly if an
internal error or some other pattern caught by gdb_test_multiple
matches.  The test doesn't really need to match specific exits/crashes
patterns, if the PASS regex is improved, like in ...

... this and the other "stepi" tests are a bit too lax, passing on
".*".  This tightens those up to expect "x/i" and the "=>" current PC
indicator, like in:

 1: x/i $pc
 => 0x3b36abc9e2 <vfork+34>:     syscall

On x86_64 Fedora 20, I now get a quick KFAIL instead of timeouts with
both the native-extended-gdbserver and native-gdbserver boards:

 PASS: gdb.base/disp-step-syscall.exp: vfork: delete break vfork
 PASS: gdb.base/disp-step-syscall.exp: vfork: continue to syscall insn vfork
 PASS: gdb.base/disp-step-syscall.exp: vfork: set displaced-stepping on
 KFAIL: gdb.base/disp-step-syscall.exp: vfork: single step over vfork (PRMS: server/13796)

and a full pass with native testing.

gdb/testsuite/
2015-03-02  Pedro Alves  <palves@redhat.com>

	* gdb.base/disp-step-syscall.exp (disp_step_cross_syscall.exp):
	Use gdb_is_target_remote instead of is_remote.  Use
	gdb_test_multiple instead of gdb_expect.  Exit early if
	gdb_test_multiple hits its internal matches.  Tighten stepi tests
	expected output.  Fail on exit with any signal, instead of just
	SIGILL.
---
 gdb/testsuite/gdb.base/disp-step-syscall.exp | 53 ++++++++++++++--------------
 1 file changed, 26 insertions(+), 27 deletions(-)

diff --git a/gdb/testsuite/gdb.base/disp-step-syscall.exp b/gdb/testsuite/gdb.base/disp-step-syscall.exp
index ff66f83..b13dce4 100644
--- a/gdb/testsuite/gdb.base/disp-step-syscall.exp
+++ b/gdb/testsuite/gdb.base/disp-step-syscall.exp
@@ -49,6 +49,8 @@ proc disp_step_cross_syscall { syscall } {
 	    return
 	}
 
+	set is_target_remote [gdb_is_target_remote]
+
 	# Delete the breakpoint on main.
 	gdb_test_no_output "delete break 1"
 
@@ -77,27 +79,34 @@ proc disp_step_cross_syscall { syscall } {
 	gdb_test "display/i \$pc" ".*"
 
 
-	# Single step until we see sysall insn or we reach the upper bound of loop
-	# iterations.
-	set see_syscall_insn 0
-
-	for {set i 0} {$i < 1000 && $see_syscall_insn == 0} {incr i} {
-	    send_gdb "stepi\n"
-	    gdb_expect {
-		-re ".*$syscall_insn.*$gdb_prompt $" {
-		    set see_syscall_insn 1
+	# Single step until we see a syscall insn or we reach the
+	# upper bound of loop iterations.
+	set msg "find syscall insn in $syscall"
+	set steps 0
+	set max_steps 1000
+	gdb_test_multiple "stepi" $msg {
+	    -re ".*$syscall_insn.*$gdb_prompt $" {
+		pass $msg
+	    }
+	    -re "x/i .*=>.*\r\n$gdb_prompt $" {
+		incr steps
+		if {$steps == $max_steps} {
+		    fail $msg
+		} else {
+		    send_gdb "stepi\n"
+		    exp_continue
 		}
-		-re ".*$gdb_prompt $" {}
 	    }
 	}
 
-	if {$see_syscall_insn == 0} then {
-	    fail "find syscall insn in $syscall"
+	if {$steps == $max_steps} {
 	    return -1
 	}
 
 	set syscall_insn_addr [get_hexadecimal_valueof "\$pc" "0"]
-	gdb_test "stepi" ".*" "stepi $syscall insn"
+	if {[gdb_test "stepi" "x/i .*=>.*" "stepi $syscall insn"] != 0} {
+	    return -1
+	}
 	set syscall_insn_next_addr [get_hexadecimal_valueof "\$pc" "0"]
 
 	gdb_test "continue" "Continuing\\..*Breakpoint \[0-9\]+, (.* in |__libc_|)$syscall \\(\\).*" \
@@ -121,22 +130,12 @@ proc disp_step_cross_syscall { syscall } {
 	gdb_test_no_output "set displaced-stepping on"
 
 	# Check the address of next instruction of syscall.
-	if {$syscall == "vfork" && [is_remote target]} {
+	if {$syscall == "vfork" && $is_target_remote} {
 	    setup_kfail server/13796 "*-*-*"
 	}
-	set test "single step over $syscall"
-	gdb_test_multiple "stepi" $test {
-	    -re "Program terminated with signal SIGILL,.*\r\n$gdb_prompt $" {
-		fail $test
-		return
-	    }
-	    -re "\\\[Inferior .* exited normally\\\].*\r\n$gdb_prompt $" {
-		fail $test
-		return
-	    }
-	    -re "\r\n$gdb_prompt $" {
-		pass $test
-	    }
+
+	if {[gdb_test "stepi" "x/i .*=>.*" "single step over $syscall"] != 0} {
+	    return -1
 	}
 
 	set syscall_insn_next_addr_found [get_hexadecimal_valueof "\$pc" "0"]
-- 
1.9.3


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH] Tighten gdb.base/disp-step-syscall.exp
  2015-03-02 20:18     ` [PATCH] Tighten gdb.base/disp-step-syscall.exp (was: Re: [PATCH v5 0/6] Remote fork events) Pedro Alves
@ 2015-03-03  6:20       ` Breazeal, Don
  2015-03-03 15:01         ` Pedro Alves
  2015-03-17 21:18       ` Breazeal, Don
  1 sibling, 1 reply; 30+ messages in thread
From: Breazeal, Don @ 2015-03-03  6:20 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches

On 3/2/2015 12:18 PM, Pedro Alves wrote:
> On 02/27/2015 12:46 AM, Don Breazeal wrote:
>>  - There are a couple of tests that show new failures that actually
>>    fail in the current mainline.  Details of these are as follows:
>>
>>    * when vfork events are enabled, gdb.base/disp-step-syscall.exp
>>      shows PASS => FAIL in .sum diffs.  The test actually always
>>      fails.  With native/master, we see
>>
>>       stepi^M
>>       FAIL: gdb.base/disp-step-syscall.exp: vfork: stepi vfork insn
>> (timeout)
>>
> 
> Hmm, I don't see that here.  I get a full pass on x86_64 Fedora 20.
> Can you try "set debug infrun 1" / "set debug lin-lwp 1" / "set debug displaced 1"
> to check if there's a gdb or kernel bug here?

I am traveling this week, so I haven't had a chance to look at this
much.  With your two patches below applied, I get a full pass on x86_64
Ubuntu 14.04 (which I hadn't tried before) and I see the timeout failure
on x86_64 Ubuntu 10.04.  Here is the relevant portion of the gdb.log
file from my failing run on Ubuntu 10.04 with the debugging turned on,
if you want to look at it.  Otherwise I will try to look at it in the
next day or so.

(gdb) PASS: gdb.base/disp-step-syscall.exp: vfork: get hexadecimal
valueof "$pc"
stepi^M
infrun: clear_proceed_status_thread (process 6486)^M
infrun: proceed (addr=0xffffffffffffffff, signal=GDB_SIGNAL_DEFAULT,
step=1)^M
infrun: resume (step=1, signal=GDB_SIGNAL_0), trap_expected=0, current
thread [process 6486] at 0x2aaaaaffcf02^M
LLR: Preparing to step process 6486, 0, inferior_ptid process 6486^M
LLR: PTRACE_SINGLESTEP process 6486, 0 (resume event thread)^M
sigchld^M
linux_nat_wait: [process -1], [TARGET_WNOHANG]^M
LLW: enter^M
LNW: waitpid(-1, ...) returned 6837, No child processes^M
LLW: waitpid 6837 received Trace/breakpoint trap (stopped)^M
LHEW: saving LWP 6837 status Trace/breakpoint trap (stopped) in
stopped_pids list^M
LNW: waitpid(-1, ...) returned 6486, No child processes^M
LLW: waitpid 6486 received Trace/breakpoint trap (stopped)^M
LLW: Handling extended status 0x02057f^M
LNW: waitpid(-1, ...) returned 0, No child processes^M
SEL: Select single-step process 6486^M
LLW: trap ptid is process 6486.^M
LLW: exit^M
sigchld^M
infrun: target_wait (-1, status) =^M
infrun:   6486 [process 6486],^M
infrun:   status->kind = vforked^M
infrun: TARGET_WAITKIND_VFORKED^M
Detaching after vfork from child process 6837.^M
sigchld^M
LCFF: waiting for VFORK_DONE on 6486^M
infrun: resume : clear step^M
infrun: resume (step=0, signal=GDB_SIGNAL_0), trap_expected=0, current
thread [process 6486] at 0x2aaaaaffcf04^M
LLR: Preparing to resume process 6486, 0, inferior_ptid process 6486^M
LLR: PTRACE_CONT process 6486, 0 (resume event thread)^M
infrun: prepare_to_wait^M
linux_nat_wait: [process -1], [TARGET_WNOHANG]^M
LLW: enter^M
LNW: waitpid(-1, ...) returned 0, No child processes^M
LLW: exit (ignore)^M
infrun: target_wait (-1, status) =^M
infrun:   -1 [process -1],^M
infrun:   status->kind = ignore^M
infrun: TARGET_WAITKIND_IGNORE^M
infrun: prepare_to_wait^M
FAIL: gdb.base/disp-step-syscall.exp: vfork: stepi vfork insn (timeout)
testcase
/scratch/dbreazea/sandbox/gdb-with-exec-6/binutils-gdb/gdb/testsuite/gdb.base/disp-step-syscall.exp
completed in 21 seconds

> 
>>      With remote and extended-remote/master, we see a bogus PASS result:
>>       stepi^M
>>       [Inferior 1 (process 9399) exited normally]^M
>>       (gdb) PASS: gdb.base/disp-step-syscall.exp: vfork: stepi vfork insn
>>
>>     The criteria to pass that test are pretty lax:
>>       gdb_test "stepi" ".*" "stepi $syscall insn"
> 
> Yeah.  I see several other problems.  Here's a patch to improve it.
> 
> Comments?

I will try to walk through this in the next day or so.

> 
> Unfortunately, with your full series applied, I get this:
> 
>  (gdb) PASS: gdb.base/disp-step-syscall.exp: vfork: get hexadecimal valueof "$pc"
>  stepi
>  Detaching from process 29944
>  Killing process(es): 29942 29944
>  /home/pedro/gdb/mygit/src/gdb/gdbserver/linux-low.c:998: A problem internal to GDBserver has been detected.
>  kill_wait_lwp: Assertion `res > 0' failed.
>  /home/pedro/gdb/mygit/src/gdb/thread.c:1182: internal-error: switch_to_thread: Assertion `inf != NULL' failed.
>  A problem internal to GDB has been detected,
>  further debugging may prove unreliable.
>  Quit this debugging session? (y or n) FAIL: gdb.base/disp-step-syscall.exp: vfork: stepi vfork insn (GDB internal error)
>  Resyncing due to internal error.
>  n

With the two patches below applied, disp-step-syscall.exp passes
consistently for me using native-extended-gdbserver on x86_64 Ubuntu
10.04 and Ubuntu 14.04.  I haven't looked (yet) at how your patches
might have caused this change in behavior, or at how I might be able to
reproduce the failure you are seeing.

I have seen the "inf != NULL" assertion before, when stopped at a remote
fork/vfork catchpoint and executing "info threads".  In that case
gdbserver was reporting the new thread created by the fork.  It was
added to the host-side thread list, but the new inferior had not been
created yet on the host side.  That specific scenario should be
prevented now in the remote follow fork patch series by not reporting
the forked child's thread until the follow_fork has been completed. (If
I am remembering that right.)

Thanks
--Don

> 
> Note, you'll need this one:
> 
>  https://sourceware.org/ml/gdb-patches/2015-03/msg00045.html
> 
> for that internal error to result in a quick bail...
> 
> ----------
> From 1f825812d3f17a2940065d0de38592700e7437bc Mon Sep 17 00:00:00 2001
> From: Pedro Alves <palves@redhat.com>
> Date: Mon, 2 Mar 2015 20:16:23 +0000
> Subject: [PATCH] Tighten gdb.base/disp-step-syscall.exp
> 
> This fixes several problems with this test.
> 
> E.g,. with --target_board=native-extended-gdbserver on
> x86_64 Fedora 20, I get:
> 
>  Running /home/pedro/gdb/mygit/src/gdb/testsuite/gdb.base/disp-step-syscall.exp ...
>  FAIL: gdb.base/disp-step-syscall.exp: vfork: get hexadecimal valueof "$pc" (timeout)
>  FAIL: gdb.base/disp-step-syscall.exp: vfork: single step over vfork final pc
>  FAIL: gdb.base/disp-step-syscall.exp: vfork: delete break vfork insn
>  FAIL: gdb.base/disp-step-syscall.exp: vfork: continue to marker (vfork) (the program is no longer running)
> 
> And with --target=native-gdbserver, I get:
> 
>  Running /home/pedro/gdb/mygit/src/gdb/testsuite/gdb.base/disp-step-syscall.exp ...
>  KPASS: gdb.base/disp-step-syscall.exp: vfork: single step over vfork (PRMS server/13796)
>  FAIL: gdb.base/disp-step-syscall.exp: vfork: get hexadecimal valueof "$pc" (timeout)
>  FAIL: gdb.base/disp-step-syscall.exp: vfork: single step over vfork final pc
>  FAIL: gdb.base/disp-step-syscall.exp: vfork: delete break vfork insn
>  FAIL: gdb.base/disp-step-syscall.exp: vfork: continue to marker (vfork) (the program is no longer running)
> 
> First, the lack of fork support on remote targets is supposed to be
> kfailed, so the KPASS is obviously bogus.  The extended-remote board
> should have KFAILed too.
> 
> The problem is that the test is using "is_remote" instead of
> gdb_is_target_remote.
> 
> And then, I get:
> 
>  (gdb) PASS: gdb.base/disp-step-syscall.exp: vfork: set displaced-stepping on
>  stepi
> 
>  Program terminated with signal SIGSEGV, Segmentation fault.
>  The program no longer exists.
>  (gdb) PASS: gdb.base/disp-step-syscall.exp: vfork: single step over vfork
> 
> Obviously, that should be a FAIL.  The problem is that the test only
> expects SIGILL, not SIGSEGV.  It also doesn't bail correctly if an
> internal error or some other pattern caught by gdb_test_multiple
> matches.  The test doesn't really need to match specific exits/crashes
> patterns, if the PASS regex is improved, like in ...
> 
> ... this and the other "stepi" tests are a bit too lax, passing on
> ".*".  This tightens those up to expect "x/i" and the "=>" current PC
> indicator, like in:
> 
>  1: x/i $pc
>  => 0x3b36abc9e2 <vfork+34>:     syscall
> 
> On x86_64 Fedora 20, I now get a quick KFAIL instead of timeouts with
> both the native-extended-gdbserver and native-gdbserver boards:
> 
>  PASS: gdb.base/disp-step-syscall.exp: vfork: delete break vfork
>  PASS: gdb.base/disp-step-syscall.exp: vfork: continue to syscall insn vfork
>  PASS: gdb.base/disp-step-syscall.exp: vfork: set displaced-stepping on
>  KFAIL: gdb.base/disp-step-syscall.exp: vfork: single step over vfork (PRMS: server/13796)
> 
> and a full pass with native testing.
> 
> gdb/testsuite/
> 2015-03-02  Pedro Alves  <palves@redhat.com>
> 
> 	* gdb.base/disp-step-syscall.exp (disp_step_cross_syscall.exp):
> 	Use gdb_is_target_remote instead of is_remote.  Use
> 	gdb_test_multiple instead of gdb_expect.  Exit early if
> 	gdb_test_multiple hits its internal matches.  Tighten stepi tests
> 	expected output.  Fail on exit with any signal, instead of just
> 	SIGILL.
> ---
>  gdb/testsuite/gdb.base/disp-step-syscall.exp | 53 ++++++++++++++--------------
>  1 file changed, 26 insertions(+), 27 deletions(-)
> 
> diff --git a/gdb/testsuite/gdb.base/disp-step-syscall.exp b/gdb/testsuite/gdb.base/disp-step-syscall.exp
> index ff66f83..b13dce4 100644
> --- a/gdb/testsuite/gdb.base/disp-step-syscall.exp
> +++ b/gdb/testsuite/gdb.base/disp-step-syscall.exp
> @@ -49,6 +49,8 @@ proc disp_step_cross_syscall { syscall } {
>  	    return
>  	}
>  
> +	set is_target_remote [gdb_is_target_remote]
> +
>  	# Delete the breakpoint on main.
>  	gdb_test_no_output "delete break 1"
>  
> @@ -77,27 +79,34 @@ proc disp_step_cross_syscall { syscall } {
>  	gdb_test "display/i \$pc" ".*"
>  
>  
> -	# Single step until we see sysall insn or we reach the upper bound of loop
> -	# iterations.
> -	set see_syscall_insn 0
> -
> -	for {set i 0} {$i < 1000 && $see_syscall_insn == 0} {incr i} {
> -	    send_gdb "stepi\n"
> -	    gdb_expect {
> -		-re ".*$syscall_insn.*$gdb_prompt $" {
> -		    set see_syscall_insn 1
> +	# Single step until we see a syscall insn or we reach the
> +	# upper bound of loop iterations.
> +	set msg "find syscall insn in $syscall"
> +	set steps 0
> +	set max_steps 1000
> +	gdb_test_multiple "stepi" $msg {
> +	    -re ".*$syscall_insn.*$gdb_prompt $" {
> +		pass $msg
> +	    }
> +	    -re "x/i .*=>.*\r\n$gdb_prompt $" {
> +		incr steps
> +		if {$steps == $max_steps} {
> +		    fail $msg
> +		} else {
> +		    send_gdb "stepi\n"
> +		    exp_continue
>  		}
> -		-re ".*$gdb_prompt $" {}
>  	    }
>  	}
>  
> -	if {$see_syscall_insn == 0} then {
> -	    fail "find syscall insn in $syscall"
> +	if {$steps == $max_steps} {
>  	    return -1
>  	}
>  
>  	set syscall_insn_addr [get_hexadecimal_valueof "\$pc" "0"]
> -	gdb_test "stepi" ".*" "stepi $syscall insn"
> +	if {[gdb_test "stepi" "x/i .*=>.*" "stepi $syscall insn"] != 0} {
> +	    return -1
> +	}
>  	set syscall_insn_next_addr [get_hexadecimal_valueof "\$pc" "0"]
>  
>  	gdb_test "continue" "Continuing\\..*Breakpoint \[0-9\]+, (.* in |__libc_|)$syscall \\(\\).*" \
> @@ -121,22 +130,12 @@ proc disp_step_cross_syscall { syscall } {
>  	gdb_test_no_output "set displaced-stepping on"
>  
>  	# Check the address of next instruction of syscall.
> -	if {$syscall == "vfork" && [is_remote target]} {
> +	if {$syscall == "vfork" && $is_target_remote} {
>  	    setup_kfail server/13796 "*-*-*"
>  	}
> -	set test "single step over $syscall"
> -	gdb_test_multiple "stepi" $test {
> -	    -re "Program terminated with signal SIGILL,.*\r\n$gdb_prompt $" {
> -		fail $test
> -		return
> -	    }
> -	    -re "\\\[Inferior .* exited normally\\\].*\r\n$gdb_prompt $" {
> -		fail $test
> -		return
> -	    }
> -	    -re "\r\n$gdb_prompt $" {
> -		pass $test
> -	    }
> +
> +	if {[gdb_test "stepi" "x/i .*=>.*" "single step over $syscall"] != 0} {
> +	    return -1
>  	}
>  
>  	set syscall_insn_next_addr_found [get_hexadecimal_valueof "\$pc" "0"]
> 

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH] Tighten gdb.base/disp-step-syscall.exp
  2015-03-03  6:20       ` [PATCH] Tighten gdb.base/disp-step-syscall.exp Breazeal, Don
@ 2015-03-03 15:01         ` Pedro Alves
  0 siblings, 0 replies; 30+ messages in thread
From: Pedro Alves @ 2015-03-03 15:01 UTC (permalink / raw)
  To: Breazeal, Don, gdb-patches

On 03/03/2015 06:20 AM, Breazeal, Don wrote:

> infrun: target_wait (-1, status) =^M
> infrun:   6486 [process 6486],^M
> infrun:   status->kind = vforked^M
> infrun: TARGET_WAITKIND_VFORKED^M
> Detaching after vfork from child process 6837.^M
> sigchld^M
> LCFF: waiting for VFORK_DONE on 6486^M
> infrun: resume : clear step^M
> infrun: resume (step=0, signal=GDB_SIGNAL_0), trap_expected=0, current
> thread [process 6486] at 0x2aaaaaffcf04^M
> LLR: Preparing to resume process 6486, 0, inferior_ptid process 6486^M
> LLR: PTRACE_CONT process 6486, 0 (resume event thread)^M
> infrun: prepare_to_wait^M
> linux_nat_wait: [process -1], [TARGET_WNOHANG]^M
> LLW: enter^M
> LNW: waitpid(-1, ...) returned 0, No child processes^M
> LLW: exit (ignore)^M
> infrun: target_wait (-1, status) =^M
> infrun:   -1 [process -1],^M
> infrun:   status->kind = ignore^M
> infrun: TARGET_WAITKIND_IGNORE^M
> infrun: prepare_to_wait^M
> FAIL: gdb.base/disp-step-syscall.exp: vfork: stepi vfork insn (timeout)
> testcase
> /scratch/dbreazea/sandbox/gdb-with-exec-6/binutils-gdb/gdb/testsuite/gdb.base/disp-step-syscall.exp
> completed in 21 seconds
> 

Odd.  Quite possibly a kernel bug.  Looks like ptrace never
reports the VFORK_DONE, or it does but SIGCHLD was never generated
and thus we got stuck in the event loop.

>>
>> Unfortunately, with your full series applied, I get this:
>>
>>  (gdb) PASS: gdb.base/disp-step-syscall.exp: vfork: get hexadecimal valueof "$pc"
>>  stepi
>>  Detaching from process 29944
>>  Killing process(es): 29942 29944
>>  /home/pedro/gdb/mygit/src/gdb/gdbserver/linux-low.c:998: A problem internal to GDBserver has been detected.
>>  kill_wait_lwp: Assertion `res > 0' failed.
>>  /home/pedro/gdb/mygit/src/gdb/thread.c:1182: internal-error: switch_to_thread: Assertion `inf != NULL' failed.
>>  A problem internal to GDB has been detected,
>>  further debugging may prove unreliable.
>>  Quit this debugging session? (y or n) FAIL: gdb.base/disp-step-syscall.exp: vfork: stepi vfork insn (GDB internal error)
>>  Resyncing due to internal error.
>>  n
> 
> With the two patches below applied, disp-step-syscall.exp passes
> consistently for me using native-extended-gdbserver on x86_64 Ubuntu
> 10.04 and Ubuntu 14.04.  I haven't looked (yet) at how your patches
> might have caused this change in behavior, or at how I might be able to
> reproduce the failure you are seeing.

TBC, I get the internal errors (F20, x86_64) without my patches too.
The only difference is that without my patches the FAIL is following by
slow timeouts:

 Running /home/pedro/gdb/mygit/src/gdb/testsuite/gdb.base/disp-step-syscall.exp ...
 FAIL: gdb.base/disp-step-syscall.exp: vfork: stepi vfork insn (GDB internal error)
 FAIL: gdb.base/disp-step-syscall.exp: vfork: get hexadecimal valueof "$pc" (timeout)
 FAIL: gdb.base/disp-step-syscall.exp: vfork: continue to vfork (3rd time) (GDB internal error)
 FAIL: gdb.base/disp-step-syscall.exp: vfork: continue to syscall insn vfork (the program is no longer running)

> 
> I have seen the "inf != NULL" assertion before, when stopped at a remote
> fork/vfork catchpoint and executing "info threads".  In that case
> gdbserver was reporting the new thread created by the fork.  It was
> added to the host-side thread list, but the new inferior had not been
> created yet on the host side.  That specific scenario should be
> prevented now in the remote follow fork patch series by not reporting
> the forked child's thread until the follow_fork has been completed. (If
> I am remembering that right.)

Adding infrun/remote logging, I see:

infrun: target_wait (-1, status) =
infrun:   26217 [Thread 26217.26217],
infrun:   status->kind = vforked
infrun: TARGET_WAITKIND_VFORKED
Sending packet: $z0,400624,1#63...Packet received: OK
Sending packet: $z0,3b36603966,1#6f...Packet received: OK
Sending packet: $z0,3b36613970,1#6b...Packet received: OK
Sending packet: $z0,3b36614891,1#6e...Packet received: OK
Sending packet: $z0,3b36abc9c0,1#23...Packet received: OK
Detaching after vfork from child process 26219.
Sending packet: $D;666b#83...Detaching from process 26219
Killing process(es): 26217 26219
/home/pedro/gdb/mygit/src/gdb/gdbserver/linux-low.c:998: A problem internal to GDBserver has been detected.
kill_wait_lwp: Assertion `res > 0' failed.
Packet received: E01
/home/pedro/gdb/mygit/src/gdb/thread.c:1182: internal-error: switch_to_thread: Assertion `inf != NULL' failed.
A problem internal to GDB has been detected,
further debugging may prove unreliable.
Quit this debugging session? (y or n) FAIL: gdb.base/disp-step-syscall.exp: vfork: stepi vfork insn (GDB internal error)
Resyncing due to internal error.


The backtrace:

...
#2  0x000000000041451e in internal_verror (file=0x456008 "/home/pedro/gdb/mygit/src/gdb/gdbserver/linux-low.c", line=998, fmt=0x455fe6 "%s: Assertion `%s' failed.",
    args=0x7fff7b828258) at /home/pedro/gdb/mygit/src/gdb/gdbserver/utils.c:106
#3  0x0000000000426ea8 in internal_error (file=0x456008 "/home/pedro/gdb/mygit/src/gdb/gdbserver/linux-low.c", line=998, fmt=0x455fe6 "%s: Assertion `%s' failed.")
    at /home/pedro/gdb/mygit/src/gdb/gdbserver/../common/errors.c:55
#4  0x00000000004293cb in kill_wait_lwp (lwp=0x14a38b0) at /home/pedro/gdb/mygit/src/gdb/gdbserver/linux-low.c:998
#5  0x0000000000429543 in linux_kill (pid=26624) at /home/pedro/gdb/mygit/src/gdb/gdbserver/linux-low.c:1050
#6  0x00000000004140ea in kill_inferior (pid=26624) at /home/pedro/gdb/mygit/src/gdb/gdbserver/target.c:219
#7  0x00000000004110e1 in detach_or_kill_inferior_callback (entry=0x14a2ad0) at /home/pedro/gdb/mygit/src/gdb/gdbserver/server.c:3087
#8  0x00000000004064da in for_each_inferior (list=0x670110 <all_processes>, action=0x41107f <detach_or_kill_inferior_callback>)
    at /home/pedro/gdb/mygit/src/gdb/gdbserver/inferiors.c:55
#9  0x0000000000411258 in detach_or_kill_for_exit () at /home/pedro/gdb/mygit/src/gdb/gdbserver/server.c:3148
#10 0x0000000000411295 in detach_or_kill_for_exit_cleanup (ignore=0x0) at /home/pedro/gdb/mygit/src/gdb/gdbserver/server.c:3163
#11 0x0000000000427178 in do_my_cleanups (pmy_chain=0x668938 <cleanup_chain>, old_chain=0x44c440 <sentinel_cleanup>)
    at /home/pedro/gdb/mygit/src/gdb/gdbserver/../common/cleanups.c:155
#12 0x00000000004271e5 in do_cleanups (old_chain=0x44c440 <sentinel_cleanup>) at /home/pedro/gdb/mygit/src/gdb/gdbserver/../common/cleanups.c:177
#13 0x00000000004276bf in throw_exception (exception=...) at /home/pedro/gdb/mygit/src/gdb/gdbserver/../common/common-exceptions.c:215
#14 0x0000000000427843 in throw_it (reason=RETURN_ERROR, error=GENERIC_ERROR, fmt=0x4480e7 "%s.", ap=0x7fff7b828648)
    at /home/pedro/gdb/mygit/src/gdb/gdbserver/../common/common-exceptions.c:274
#15 0x000000000042786d in throw_verror (error=GENERIC_ERROR, fmt=0x4480e7 "%s.", ap=0x7fff7b828648)
    at /home/pedro/gdb/mygit/src/gdb/gdbserver/../common/common-exceptions.c:280
#16 0x0000000000414456 in verror (string=0x4480e7 "%s.", args=0x7fff7b828648) at /home/pedro/gdb/mygit/src/gdb/gdbserver/utils.c:85
#17 0x0000000000426e00 in error (fmt=0x4480e7 "%s.") at /home/pedro/gdb/mygit/src/gdb/gdbserver/../common/errors.c:43
#18 0x0000000000414431 in perror_with_name (string=0x4435a6 "Can't determine port") at /home/pedro/gdb/mygit/src/gdb/gdbserver/utils.c:71
#19 0x0000000000407e49 in remote_open (name=0x7fff7b82a6fc ":2347") at /home/pedro/gdb/mygit/src/gdb/gdbserver/remote-utils.c:389
#20 0x0000000000411b12 in captured_main (argc=4, argv=0x7fff7b828a68) at /home/pedro/gdb/mygit/src/gdb/gdbserver/server.c:3414
#21 0x0000000000411ca2 in main (argc=4, argv=0x7fff7b828a68) at /home/pedro/gdb/mygit/src/gdb/gdbserver/server.c:3490
...

Seems like gdb disconnects, and we end up in remote_open again.
Then probably due to --once (the list descriptor is closed), that
fails and throws, which runs the "kill or detach everything" cleanup
(detach_or_kill_for_exit_cleanup).  And that ends up in your new
code here:

static int
linux_kill (int pid)
{
  struct process_info *process;
  struct lwp_info *lwp;
  struct target_waitstatus last;
  ptid_t last_ptid;

  /* If we're stopped while forking and we haven't followed yet,
     kill the child task.  We need to do this first because the
     parent will be sleeping if this is a vfork.  */

  get_last_target_status (&last_ptid, &last);

  if (last.kind == TARGET_WAITKIND_FORKED
      || last.kind == TARGET_WAITKIND_VFORKED)
    {
      lwp = find_lwp_pid (last.value.related_pid);
      gdb_assert (lwp != NULL);
      kill_wait_lwp (lwp);
      process = find_process_pid (ptid_get_pid (last.value.related_pid));
      the_target->mourn (process);
    }

trying to kill the vfork child.

Really get_last_target_status is not a good idea.  It's broken
on the native side already, and adding it to gdbserver too is
not a good idea.  E.g., consider scheduler-locking or non-stop.
Other events on other processes/threads can easily happen
and thus overwrite the last target status, before something
decides to kill the fork parent.

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH v6 1/6] Identify remote fork event support
  2015-03-17 20:56     ` [PATCH v6 0/6] Remote fork events Don Breazeal
@ 2015-03-17 20:56       ` Don Breazeal
  2015-03-24 10:57         ` Pedro Alves
  2015-03-17 20:57       ` [PATCH v6 3/6] Extended-remote Linux follow fork Don Breazeal
                         ` (4 subsequent siblings)
  5 siblings, 1 reply; 30+ messages in thread
From: Don Breazeal @ 2015-03-17 20:56 UTC (permalink / raw)
  To: gdb-patches, palves

Hi Pedro,
This patch is virtually unchanged from the previous version except
for accommodating new changes on the mainline.

Thanks!
--Don

This patch implements a mechanism for GDB to determine whether fork
events are supported in gdbserver.  This is a preparatory patch for
remote fork and exec event support.

Two new RSP packets are defined to represent fork and vfork event
support.  These packets are used just like PACKET_multiprocess_feature
to denote whether the corresponding event is supported.  GDB sends
fork-events+ and vfork-events+ to gdbserver to inquire about fork
event support.  If the response enables these packets, then GDB
knows that gdbserver supports the corresponding events and will
enable them.

Target functions used to query for support are included along with
each new packet.

In order for gdbserver to know whether the events are supported at the
point where the qSupported packet arrives, the code in nat/linux-ptrace.c
had to be reorganized.  Previously it would test for fork/exec event
support, then enable the events using the pid of the inferior.  When the
qSupported packet arrives there may not be an inferior.  So the mechanism
was split into two parts: a function that checks whether the events are
supported, called when gdbserver starts up, and another that enables the
events when the inferior stops for the first time.

Another gdbserver change was to add some global variables similar to
multi_process, one per new packet.  These are used to control whether
the corresponding fork events are enabled.  If GDB does not inquire
about the event support in the qSupported packet, then gdbserver will
not set these "report the event" flags.  If the flags are not set, the
events are ignored like they were in the past.  Thus, gdbserver will
never send fork event notification to an older GDB that doesn't
recognize fork events.

Tested on Ubuntu x64, native/remote/extended-remote, and as part of
subsequent patches in the series.

gdb/doc/
2015-03-17  Don Breazeal  <donb@codesourcery.com>

	* gdb.texinfo (General Query Packets): Add remote protocol
	features 'fork-events' and 'vfork-events'.

gdb/gdbserver/
2015-03-17  Don Breazeal  <donb@codesourcery.com>

	* linux-low.c (linux_supports_fork_events): New function.
	(linux_supports_vfork_events): New function.
	(linux_target_ops): Initialize new structure members.
	(initialize_low): Call linux_check_ptrace_features.
	* lynx-low.c (lynx_target_ops): Initialize new structure
	members.
	* server.c (report_fork_events, report_vfork_events):
	New global flags.
	(handle_query): Add new features to qSupported packet.
	(captured_main): Initialize new global variables.
	* target.h (struct target_ops) <supports_fork_events>:
	New member.
	<supports_vfork_events>: New member.
	(target_supports_fork_events): New macro.
	(target_supports_vfork_events): New macro.
	* win32-low.c (win32_target_ops): Initialize new structure
	members.

gdb/
2015-03-17  Don Breazeal  <donb@codesourcery.com>

	* NEWS: Announce new fork event feature support in
	qSupported packet.
	* nat/linux-ptrace.c (linux_check_ptrace_features): Change
	from static to extern.
	* nat/linux-ptrace.h (linux_check_ptrace_features): Declare.
	* remote.c (anonymous enum): <PACKET_fork_event_feature,
	* PACKET_vfork_event_feature>: New enumeration constants.
	(remote_query_supported): Add new feature queries to qSupported
	packet.
	(remote_protocol_features): Add table entries for new packets.
	(_initialize_remote): Exempt new packets from the requirement
	to have 'set remote' commands.

---
 gdb/NEWS                  |  3 +++
 gdb/doc/gdb.texinfo       | 12 ++++++++++++
 gdb/gdbserver/linux-low.c | 21 +++++++++++++++++++++
 gdb/gdbserver/lynx-low.c  |  2 ++
 gdb/gdbserver/server.c    | 22 ++++++++++++++++++++++
 gdb/gdbserver/target.h    | 14 ++++++++++++++
 gdb/gdbserver/win32-low.c |  2 ++
 gdb/nat/linux-ptrace.c    |  2 +-
 gdb/nat/linux-ptrace.h    |  1 +
 gdb/remote.c              | 21 ++++++++++++++++++++-
 10 files changed, 98 insertions(+), 2 deletions(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index bda4a35..d6c8769 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -92,6 +92,9 @@ vFile:fstat:
 HP/PA running HP-UX           hppa*-*-hpux*
 Itanium running HP-UX         ia64-*-hpux*
 
+* The remote stub now reports support for fork and vfork events to GDB's
+qSupported query.
+
 *** Changes in GDB 7.9
 
 * GDB now supports hardware watchpoints on x86 GNU Hurd.
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 9e71642..b089b1b 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -35806,6 +35806,18 @@ extensions unless the stub also reports that it supports them by
 including @samp{multiprocess+} in its @samp{qSupported} reply.
 @xref{multiprocess extensions}, for details.
 
+@item fork-events
+This feature indicates whether @value{GDBN} supports fork event 
+extensions to the remote protocol.  @value{GDBN} does not use such
+extensions unless the stub also reports that it supports them by
+including @samp{fork-events+} in its @samp{qSupported} reply.
+
+@item vfork-events
+This feature indicates whether @value{GDBN} supports vfork event 
+extensions to the remote protocol.  @value{GDBN} does not use such
+extensions unless the stub also reports that it supports them by
+including @samp{vfork-events+} in its @samp{qSupported} reply.
+
 @item xmlRegisters
 This feature indicates that @value{GDBN} supports the XML target
 description.  If the stub sees @samp{xmlRegisters=} with target
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 48d905b..c710344 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -5255,6 +5255,22 @@ linux_supports_multi_process (void)
   return 1;
 }
 
+/* Check if fork events are supported.  */
+
+static int
+linux_supports_fork_events (void)
+{
+  return linux_supports_tracefork ();
+}
+
+/* Check if vfork events are supported.  */
+
+static int
+linux_supports_vfork_events (void)
+{
+  return linux_supports_tracefork ();
+}
+
 static int
 linux_supports_disable_randomization (void)
 {
@@ -6219,6 +6235,8 @@ static struct target_ops linux_target_ops = {
   linux_async,
   linux_start_non_stop,
   linux_supports_multi_process,
+  linux_supports_fork_events,
+  linux_supports_vfork_events,
 #ifdef USE_THREAD_DB
   thread_db_handle_monitor_command,
 #else
@@ -6295,4 +6313,7 @@ initialize_low (void)
   sigaction (SIGCHLD, &sigchld_action, NULL);
 
   initialize_low_arch ();
+
+  /* Enable extended ptrace events.  */
+  linux_check_ptrace_features ();
 }
diff --git a/gdb/gdbserver/lynx-low.c b/gdb/gdbserver/lynx-low.c
index 2f85829..687cce0 100644
--- a/gdb/gdbserver/lynx-low.c
+++ b/gdb/gdbserver/lynx-low.c
@@ -758,6 +758,8 @@ static struct target_ops lynx_target_ops = {
   NULL,  /* async */
   NULL,  /* start_non_stop */
   NULL,  /* supports_multi_process */
+  NULL,  /* supports_fork_events */
+  NULL,  /* supports_vfork_events */
   NULL,  /* handle_monitor_command */
 };
 
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index 9ff2f8e..ee865fa 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -57,6 +57,8 @@ static int exit_requested;
 int run_once;
 
 int multi_process;
+int report_fork_events;
+int report_vfork_events;
 int non_stop;
 int swbreak_feature;
 int hwbreak_feature;
@@ -1992,6 +1994,18 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
 		  if (target_supports_stopped_by_hw_breakpoint ())
 		    hwbreak_feature = 1;
 		}
+	      else if (strcmp (p, "fork-events+") == 0)
+		{
+		  /* GDB supports and wants fork events if possible.  */
+		  if (target_supports_fork_events ())
+		    report_fork_events = 1;
+		}
+	      else if (strcmp (p, "vfork-events+") == 0)
+		{
+		  /* GDB supports and wants vfork events if possible.  */
+		  if (target_supports_vfork_events ())
+		    report_vfork_events = 1;
+		}
 	      else
 		target_process_qsupported (p);
 
@@ -2042,6 +2056,12 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
       if (target_supports_multi_process ())
 	strcat (own_buf, ";multiprocess+");
 
+      if (target_supports_fork_events ())
+	strcat (own_buf, ";fork-events+");
+
+      if (target_supports_vfork_events ())
+	strcat (own_buf, ";vfork-events+");
+
       if (target_supports_non_stop ())
 	strcat (own_buf, ";QNonStop+");
 
@@ -3390,6 +3410,8 @@ captured_main (int argc, char *argv[])
 
       noack_mode = 0;
       multi_process = 0;
+      report_fork_events = 0;
+      report_vfork_events = 0;
       /* Be sure we're out of tfind mode.  */
       current_traceframe = -1;
       cont_thread = null_ptid;
diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
index 126c861..dc51627 100644
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -277,6 +277,12 @@ struct target_ops
   /* Returns true if the target supports multi-process debugging.  */
   int (*supports_multi_process) (void);
 
+  /* Returns true if fork events are supported.  */
+  int (*supports_fork_events) (void);
+
+  /* Returns true if vfork events are supported.  */
+  int (*supports_vfork_events) (void);
+
   /* If not NULL, target-specific routine to process monitor command.
      Returns 1 if handled, or 0 to perform default processing.  */
   int (*handle_monitor_command) (char *);
@@ -408,6 +414,14 @@ void set_target_ops (struct target_ops *);
 
 int kill_inferior (int);
 
+#define target_supports_fork_events() \
+  (the_target->supports_fork_events ? \
+   (*the_target->supports_fork_events) () : 0)
+
+#define target_supports_vfork_events() \
+  (the_target->supports_vfork_events ? \
+   (*the_target->supports_vfork_events) () : 0)
+
 #define detach_inferior(pid) \
   (*the_target->detach) (pid)
 
diff --git a/gdb/gdbserver/win32-low.c b/gdb/gdbserver/win32-low.c
index 6c86765..5f50e46 100644
--- a/gdb/gdbserver/win32-low.c
+++ b/gdb/gdbserver/win32-low.c
@@ -1827,6 +1827,8 @@ static struct target_ops win32_target_ops = {
   NULL, /* async */
   NULL, /* start_non_stop */
   NULL, /* supports_multi_process */
+  NULL, /* supports_fork_events */
+  NULL, /* supports_vfork_events */
   NULL, /* handle_monitor_command */
   NULL, /* core_of_thread */
   NULL, /* read_loadmap */
diff --git a/gdb/nat/linux-ptrace.c b/gdb/nat/linux-ptrace.c
index 2244d9d..aba3da8 100644
--- a/gdb/nat/linux-ptrace.c
+++ b/gdb/nat/linux-ptrace.c
@@ -337,7 +337,7 @@ static void linux_test_for_exitkill (int child_pid);
 
 /* Determine ptrace features available on this target.  */
 
-static void
+void
 linux_check_ptrace_features (void)
 {
   int child_pid, ret, status;
diff --git a/gdb/nat/linux-ptrace.h b/gdb/nat/linux-ptrace.h
index 8354a4d..03d98c9 100644
--- a/gdb/nat/linux-ptrace.h
+++ b/gdb/nat/linux-ptrace.h
@@ -149,6 +149,7 @@ extern void linux_ptrace_attach_fail_reason (pid_t pid, struct buffer *buffer);
 extern char *linux_ptrace_attach_fail_reason_string (ptid_t ptid, int err);
 
 extern void linux_ptrace_init_warnings (void);
+extern void linux_check_ptrace_features (void);
 extern void linux_enable_event_reporting (pid_t pid, int attached);
 extern void linux_disable_event_reporting (pid_t pid);
 extern int linux_supports_tracefork (void);
diff --git a/gdb/remote.c b/gdb/remote.c
index 9aaee13..85ced68 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -1350,6 +1350,12 @@ enum {
   /* Support for hwbreak+ feature.  */
   PACKET_hwbreak_feature,
 
+  /* Support for fork events.  */
+  PACKET_fork_event_feature,
+
+  /* Support for vfork events.  */
+  PACKET_vfork_event_feature,
+
   PACKET_MAX
 };
 
@@ -4051,6 +4057,10 @@ static const struct protocol_feature remote_protocol_features[] = {
   { "hwbreak", PACKET_DISABLE, remote_supported_packet, PACKET_hwbreak_feature },
   { "vFile:fstat", PACKET_DISABLE, remote_supported_packet,
     PACKET_vFile_fstat },
+  { "fork-events", PACKET_DISABLE, remote_supported_packet,
+    PACKET_fork_event_feature },
+  { "vfork-events", PACKET_DISABLE, remote_supported_packet,
+    PACKET_vfork_event_feature },
 };
 
 static char *remote_support_xml;
@@ -4129,6 +4139,12 @@ remote_query_supported (void)
 
       q = remote_query_supported_append (q, "qRelocInsn+");
 
+      if (rs->extended)
+	{
+	  q = remote_query_supported_append (q, "fork-events+");
+	  q = remote_query_supported_append (q, "vfork-events+");
+	}
+
       q = reconcat (q, "qSupported:", q, (char *) NULL);
       putpkt (q);
 
@@ -12508,7 +12524,8 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
   add_packet_config_cmd (&remote_protocol_packets[PACKET_hwbreak_feature],
                          "hwbreak-feature", "hwbreak-feature", 0);
 
-  /* Assert that we've registered commands for all packet configs.  */
+  /* Assert that we've registered "set remote foo-packet" commands
+     for all packet configs.  */
   {
     int i;
 
@@ -12527,6 +12544,8 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
 	  case PACKET_DisconnectedTracing_feature:
 	  case PACKET_augmented_libraries_svr4_read_feature:
 	  case PACKET_qCRC:
+	  case PACKET_fork_event_feature:
+	  case PACKET_vfork_event_feature:
 	    /* Additions to this list need to be well justified:
 	       pre-existing packets are OK; new packets are not.  */
 	    excepted = 1;
-- 
1.8.1.1

^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH v6 0/6] Remote fork events
  2015-02-27  0:46   ` [PATCH v5 0/6] " Don Breazeal
                       ` (7 preceding siblings ...)
  2015-03-02 20:18     ` [PATCH] Tighten gdb.base/disp-step-syscall.exp (was: Re: [PATCH v5 0/6] Remote fork events) Pedro Alves
@ 2015-03-17 20:56     ` Don Breazeal
  2015-03-17 20:56       ` [PATCH v6 1/6] Identify remote fork event support Don Breazeal
                         ` (5 more replies)
  8 siblings, 6 replies; 30+ messages in thread
From: Don Breazeal @ 2015-03-17 20:56 UTC (permalink / raw)
  To: gdb-patches, palves

This is an update to the patch series implementing remote follow-fork.
This update only implements fork and vfork events for extended remote,
omitting exec events and 'target remote' for now.

Changes from the previous version are relatively few:
 * In patch 6, in the case of a 'kill' operation on a fork parent before
   follow_fork has been completed, implement killing the fork child on
   the host side instead of in gdbserver, and don't implement
   get_last_target_status in gdbserver.  In particular this fixes
   failures seen in gdb.base/disp-step-syscall.exp in previous versions
   of the patchset.

 * Eliminate a redundant assignment in
   gdbserver/linux-low.c:handle_extended_wait

 * Merge changes from mainline

The new series includes six patches as follows:

1/6: Preparatory patch that implements qSupported support for fork events
     and associated mechanisms.

2/6: Implements functions to clone breakpoint lists in gdbserver.

3/6: Implements follow fork for 'fork' but not 'vfork', for
     extended-remote targets only.

4/6: Adds the architecture-specific pieces of follow-fork that allows
     hardware watchpoints to be inherited by a forked child.
     This patch was previously approved, although I have added a
     trivial change to gdb/NEWS in this patch.

5/6: Adds follow fork for 'vfork'.

6/6: Adds catchpoints for 'fork' and 'vfork', along with support for
     killing a process that has forked before follow_fork is completed.

TESTING:
Testing was mostly done using x86_64 Ubuntu, with the exception of the
architecture-specific patch, #4.  There are a few anomalies that show
up, but don't signify any problem.

 - Intermediate patches show failures due to the lack of features
   implemented in subsequent patches, like missing hardware watchpoint
   or catchpoint support.

 - Some vfork tests fail due to the lack of exec event support.

 - There are a couple of tests that show new failures that actually
   fail in the current mainline.  Details of these are as follows:

  * Once vfork events are enabled, gdb.base/watch-vfork.exp
    shows PASS => FAIL in .sum diffs.  This test always fails.  With
    native/master, we see this:

     continue^M
     Continuing.^M
     FAIL: gdb.base/watch-vfork.exp: Watchpoint triggers after vfork
(sw) (timeout)

    With extended-remote/master, we see this:
     continue
     Continuing.
     [Inferior 1 (process 18866) exited normally]
     (gdb) FAIL: gdb.base/watch-vfork.exp: Watchpoint triggers after
vfork (sw) (the program exited)

    But once vfork events are enabled, we see:
     continue
     Continuing.
     Detaching from process 17405
     FAIL: gdb.base/watch-vfork.exp: Watchpoint triggers after vfork
(sw) (timeout)
    which more-or-less matches the native/master behavior.

Thanks,
--Don

^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH v6 2/6] Clone remote breakpoints
  2015-03-17 20:56     ` [PATCH v6 0/6] Remote fork events Don Breazeal
                         ` (2 preceding siblings ...)
  2015-03-17 20:57       ` [PATCH v6 4/6] Arch-specific remote " Don Breazeal
@ 2015-03-17 20:57       ` Don Breazeal
  2015-03-17 20:58       ` [PATCH v6 5/6] Remote follow vfork Don Breazeal
  2015-03-17 20:58       ` [PATCH v6 6/6] Remote fork catch Don Breazeal
  5 siblings, 0 replies; 30+ messages in thread
From: Don Breazeal @ 2015-03-17 20:57 UTC (permalink / raw)
  To: gdb-patches, palves

This version of this patch is unchanged except possibly for merges from
the mainline.  The last review+response was here:
https://sourceware.org/ml/gdb-patches/2014-10/msg00883.html.

Thanks!
--Don

This patch implements gdbserver routines to clone the breakpoint lists of a
process, duplicating them for another process.  In gdbserver, each process
maintains its own independent breakpoint list.  When a fork call creates a
child, all of the breakpoints currently inserted in the parent process are
also inserted in the child process, but there is nothing to describe them
in the data structures related to the child.  The child must have a
breakpoint list describing them so that they can be removed (if detaching)
or recognized (if following).  Implementation is a mechanical process of
just cloning the lists in several new functions in gdbserver/mem-break.c.

Tested by building, since none of the new functions are called yet.  This
was tested with another patch in the series that implements follow-fork.

gdb/gdbserver/
2015-03-17  Don Breazeal  <donb@codesourcery.com>

        * mem-break.c (APPEND_TO_LIST): Define macro.
        (clone_agent_expr): New function.
        (clone_one_breakpoint): New function.
        (clone_all_breakpoints): New function.
        * mem-break.h: Declare new functions.

---
 gdb/gdbserver/mem-break.c | 105 ++++++++++++++++++++++++++++++++++++++++++++++
 gdb/gdbserver/mem-break.h |   6 +++
 2 files changed, 111 insertions(+)

diff --git a/gdb/gdbserver/mem-break.c b/gdb/gdbserver/mem-break.c
index 70fab2e..d1b66bf 100644
--- a/gdb/gdbserver/mem-break.c
+++ b/gdb/gdbserver/mem-break.c
@@ -28,6 +28,24 @@ int breakpoint_len;
 
 #define MAX_BREAKPOINT_LEN 8
 
+/* Helper macro used in loops that append multiple items to a singly-linked
+   list instead of inserting items at the head of the list, as, say, in the
+   breakpoint lists.  LISTPP is a pointer to the pointer that is the head of
+   the new list.  ITEMP is a pointer to the item to be added to the list.
+   TAILP must be defined to be the same type as ITEMP, and initialized to
+   NULL.  */
+
+#define APPEND_TO_LIST(listpp, itemp, tailp) \
+	  do \
+	    { \
+	      if ((tailp) == NULL) \
+		*(listpp) = (itemp); \
+	      else \
+		(tailp)->next = (itemp); \
+	      (tailp) = (itemp); \
+	    } \
+	  while (0)
+
 /* GDB will never try to install multiple breakpoints at the same
    address.  However, we can see GDB requesting to insert a breakpoint
    at an address is had already inserted one previously in a few
@@ -1913,3 +1931,90 @@ free_all_breakpoints (struct process_info *proc)
   while (proc->breakpoints)
     delete_breakpoint_1 (proc, proc->breakpoints);
 }
+
+/* Clone an agent expression.  */
+
+static struct agent_expr *
+clone_agent_expr (const struct agent_expr *src_ax)
+{
+  struct agent_expr *ax;
+
+  ax = xcalloc (1, sizeof (*ax));
+  ax->length = src_ax->length;
+  ax->bytes = xcalloc (ax->length, 1);
+  memcpy (ax->bytes, src_ax->bytes, ax->length);
+  return ax;
+}
+
+/* Deep-copy the contents of one breakpoint to another.  */
+
+static struct breakpoint *
+clone_one_breakpoint (const struct breakpoint *src)
+{
+  struct breakpoint *dest;
+  struct raw_breakpoint *dest_raw;
+  struct point_cond_list *current_cond;
+  struct point_cond_list *new_cond;
+  struct point_cond_list *cond_tail = NULL;
+  struct point_command_list *current_cmd;
+  struct point_command_list *new_cmd;
+  struct point_command_list *cmd_tail = NULL;
+
+  /* Clone the raw breakpoint.  */
+  dest_raw = xcalloc (1, sizeof (*dest_raw));
+  dest_raw->raw_type = src->raw->raw_type;
+  dest_raw->refcount = src->raw->refcount;
+  dest_raw->pc = src->raw->pc;
+  dest_raw->size = src->raw->size;
+  memcpy (dest_raw->old_data, src->raw->old_data, MAX_BREAKPOINT_LEN);
+  dest_raw->inserted = src->raw->inserted;
+
+  /* Clone the high-level breakpoint.  */
+  dest = xcalloc (1, sizeof (*dest));
+  dest->type = src->type;
+  dest->raw = dest_raw;
+  dest->handler = src->handler;
+
+  /* Clone the condition list.  */
+  for (current_cond = src->cond_list; current_cond != NULL;
+       current_cond = current_cond->next)
+    {
+      new_cond = xcalloc (1, sizeof (*new_cond));
+      new_cond->cond = clone_agent_expr (current_cond->cond);
+      APPEND_TO_LIST (&dest->cond_list, new_cond, cond_tail);
+    }
+
+  /* Clone the command list.  */
+  for (current_cmd = src->command_list; current_cmd != NULL;
+       current_cmd = current_cmd->next)
+    {
+      new_cmd = xcalloc (1, sizeof (*new_cmd));
+      new_cmd->cmd = clone_agent_expr (current_cmd->cmd);
+      new_cmd->persistence = current_cmd->persistence;
+      APPEND_TO_LIST (&dest->command_list, new_cmd, cmd_tail);
+    }
+
+  return dest;
+}
+
+/* Create a new breakpoint list NEW_LIST that is a copy of the
+   list starting at SRC_LIST.  Create the corresponding new
+   raw_breakpoint list NEW_RAW_LIST as well.  */
+
+void
+clone_all_breakpoints (struct breakpoint **new_list,
+		       struct raw_breakpoint **new_raw_list,
+		       const struct breakpoint *src_list)
+{
+  const struct breakpoint *bp;
+  struct breakpoint *new_bkpt;
+  struct breakpoint *bkpt_tail = NULL;
+  struct raw_breakpoint *raw_bkpt_tail = NULL;
+
+  for (bp = src_list; bp != NULL; bp = bp->next)
+    {
+      new_bkpt = clone_one_breakpoint (bp);
+      APPEND_TO_LIST (new_list, new_bkpt, bkpt_tail);
+      APPEND_TO_LIST (new_raw_list, new_bkpt->raw, raw_bkpt_tail);
+    }
+}
diff --git a/gdb/gdbserver/mem-break.h b/gdb/gdbserver/mem-break.h
index 8b010c1..b5a3208 100644
--- a/gdb/gdbserver/mem-break.h
+++ b/gdb/gdbserver/mem-break.h
@@ -253,4 +253,10 @@ int insert_memory_breakpoint (struct raw_breakpoint *bp);
 
 int remove_memory_breakpoint (struct raw_breakpoint *bp);
 
+/* Create a new breakpoint list NEW_BKPT_LIST that is a copy of SRC.  */
+
+void clone_all_breakpoints (struct breakpoint **new_bkpt_list,
+			    struct raw_breakpoint **new_raw_bkpt_list,
+			    const struct breakpoint *src);
+
 #endif /* MEM_BREAK_H */
-- 
1.8.1.1

^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH v6 3/6] Extended-remote Linux follow fork
  2015-03-17 20:56     ` [PATCH v6 0/6] Remote fork events Don Breazeal
  2015-03-17 20:56       ` [PATCH v6 1/6] Identify remote fork event support Don Breazeal
@ 2015-03-17 20:57       ` Don Breazeal
  2015-03-24 12:17         ` Pedro Alves
  2015-03-17 20:57       ` [PATCH v6 4/6] Arch-specific remote " Don Breazeal
                         ` (3 subsequent siblings)
  5 siblings, 1 reply; 30+ messages in thread
From: Don Breazeal @ 2015-03-17 20:57 UTC (permalink / raw)
  To: gdb-patches, palves

Hi Pedro,

This patch is mostly unchanged from the previous version.  I did make
a change in handle_extended_wait where I was doing a redundant pointer
assignment.  In fixing that I changed the name of the function argument
'event_child' to 'event_lwp'.  'event_child' was ambiguous, since it
could be the fork parent.

Thanks!
--Don

This patch implements basic support for follow-fork and detach-on-fork on
extended-remote Linux targets.  Only 'fork' is supported in this patch;
'vfork' support is added n a subsequent patch.  This patch depends on 
the previous patches in the patch series.

Sufficient extended-remote functionality has been implemented here to pass
gdb.base/multi-forks.exp, as well as gdb.base/foll-fork.exp with the
catchpoint tests commented out.  Some other fork tests fail with this
patch because it doesn't provide the architecture support needed for
watchpoint inheritance or fork catchpoints.

The implementation follows the same general structure as for the native
implementation as much as possible.

This implementation included:
 * enabling fork events in linux-low.c in initialize_low and
   linux_enable_extended_features

 * handling fork events in gdbserver/linux-low.c:handle_extended_wait

   - when a fork event occurs in gdbserver, we must do the full creation
     of the new process, thread, lwp, and breakpoint lists.  This is
     required whether or not the new child is destined to be
     detached-on-fork, because GDB will make target calls that require all
     the structures.  In particular we need the breakpoint lists in order
     to remove the breakpoints from a detaching child.  If we are not
     detaching the child we will need all these structures anyway.

   - as part of this event handling we store the target_waitstatus in a new
     member of the parent thread_info structure, 'pending_follow'.  This
     is used to store extended event information for reporting to GDB.

   - handle_extended_wait is given a return value, denoting whether the
     handled event should be reported to GDB.  Previously it had only
     handled clone events, which were never reported.

 * using a new predicate in gdbserver to control handling of the fork event
   (and eventually all extended events) in linux_wait_1.  The predicate,
   extended_event_reported, checks a target_waitstatus.kind for an
   extended ptrace event.

 * implementing a new RSP 'T' Stop Reply Packet stop reason: "fork", in
   gdbserver/remote-utils.c and remote.c.

 * implementing new target and RSP support for target_follow_fork with
   target extended-remote.  (The RSP components were actually defined in
   patch 4, but they see their first use here).

   - remote target routine remote_follow_fork, which just sends the 'D;pid'
     detach packet to detach the new fork child cleanly.  We can't just
     call target_detach because the data structures for the forked child
     have not been allocated on the host side.

Tested on x64 Ubuntu Lucid, native, remote, extended-remote.

gdb/doc/
2015-03-17  Don Breazeal  <donb@codesourcery.com>

	* gdb.texinfo (Forks): List targets that have fork debugging
	support.
	(Stop Reply Packets): List new stop reason "xfork".

gdb/gdbserver/
2015-03-17  Don Breazeal  <donb@codesourcery.com>

	* linux-low.c (handle_extended_wait): Implement return value,
	handle PTRACE_EVENT_FORK.
	(linux_low_enable_events): New function.
	(linux_low_filter_event): Use return value from
	handle_extended_wait.
	(extended_event_reported): New function.
	(linux_wait_1): Call extended_event_reported.
	(linux_write_memory): Add pid to debug message.
	(reset_lwp_ptrace_options_callback): New function.
	(linux_handle_new_gdb_connection): New function.
	(linux_target_ops): Initialize new structure member.
	(initialize_low): Call linux_ptrace_set_additional_flags.
	* linux-low.h (struct lwp_info) <waitstatus>: New member.
	* lynx-low.c: Initialize new structure member.
	* remote-utils.c (prepare_resume_reply): Implement stop reason
	"xfork" for "T" stop message.
	* server.h (report_fork_events): Declare global flag.
	* target.h (struct target_ops) <handle_new_gdb_connection>:
	New member.
	* win32-low.c: Initialize new structure member.

gdb/
2015-03-17  Don Breazeal  <donb@codesourcery.com>

	* NEWS: Announce follow fork support and associated RSP
	addition.
	* nat/linux-ptrace.c (supported_additional_flags): New
	static variable.
	(linux_check_ptrace_features): Use supported_additional_flags.
	(linux_test_for_tracesysgood): Likewise.
	(linux_test_for_tracefork): Likewise.
	(linux_reset_ptrace_options): New function.
	(linux_ptrace_clear_flags): New function.
	* nat/linux-ptrace.h: (linux_ptrace_clear_flags): Declare new
	function.
	* remote.c (remote_fork_event_p): New function.
	(remote_detach_1): Add target_ops argument, don't mourn inferior
	if doing detach-on-fork.
	(remote_detach): Add target_ops argument, pass to remote_detach_1.
	(extended_remote_detach): Add target_ops argument, pass to
	remote_detach_1.
	(remote_follow_fork): New function.
	(remote_parse_stop_reply): Handle new "T" stop reason "xfork".
	(remote_pid_to_str): Print "process" strings for pid/0/0 ptids.
	(init_extended_remote_ops): Initialize to_follow_fork.

gdb/testsuite/
2015-03-17  Don Breazeal  <donb@codesourcery.com>

	* gdb.base/multi-forks.exp (continue_to_exit_bp_loc):
	Use '-i' in expect statement to include input from forked child.
	Ignore inferior output if gdb,noinferiorio.
	* lib/gdb.exp (server_spawn_id): Declare new global
	variable.
	* lib/gdbserver-support.exp (server_spawn_id): Make
	variable global.

---
 gdb/NEWS                                |  15 ++-
 gdb/doc/gdb.texinfo                     |   9 ++
 gdb/gdbserver/linux-low.c               | 170 +++++++++++++++++++++++++++++---
 gdb/gdbserver/linux-low.h               |   4 +
 gdb/gdbserver/lynx-low.c                |   1 +
 gdb/gdbserver/remote-utils.c            |  15 ++-
 gdb/gdbserver/server.c                  |   3 +
 gdb/gdbserver/server.h                  |   1 +
 gdb/gdbserver/target.h                  |   7 ++
 gdb/gdbserver/win32-low.c               |   1 +
 gdb/linux-nat.c                         |   6 +-
 gdb/nat/linux-ptrace.c                  |  59 +++++++++--
 gdb/nat/linux-ptrace.h                  |  11 +++
 gdb/remote.c                            |  98 +++++++++++++++---
 gdb/testsuite/gdb.base/multi-forks.exp  |  22 ++++-
 gdb/testsuite/lib/gdb.exp               |   3 +
 gdb/testsuite/lib/gdbserver-support.exp |   2 +
 17 files changed, 382 insertions(+), 45 deletions(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index d6c8769..5aedda5 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -76,6 +76,10 @@ hwbreak stop reason
 vFile:fstat:
   Return information about files on the remote system.
 
+T Stop Reply Packet's reason
+  The T stop reply packet supports a new stop reason 'xfork', which
+  signifies that the specified inferior has executed a fork.
+
 * The info record command now shows the recording format and the
   branch tracing configuration for the current thread when using
   the btrace record target.
@@ -92,8 +96,15 @@ vFile:fstat:
 HP/PA running HP-UX           hppa*-*-hpux*
 Itanium running HP-UX         ia64-*-hpux*
 
-* The remote stub now reports support for fork and vfork events to GDB's
-qSupported query.
+* Remote fork events
+
+  The remote stub now reports support for fork and vfork events to
+  GDB's qSupported query.
+
+  GDBserver extended-remote Linux targets now provides basic support
+  for fork events.  This enables follow-fork-mode and detach-on-fork
+  for those targets with Linux kernels 2.5.60 and later.  Hardware
+  watchpoints are not inherited across a fork in this implementation.
 
 *** Changes in GDB 7.9
 
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index b089b1b..b62b37f 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -3142,6 +3142,9 @@ create additional processes using the @code{fork} or @code{vfork} functions.
 Currently, the only platforms with this feature are HP-UX (11.x and later
 only?) and @sc{gnu}/Linux (kernel version 2.5.60 and later).
 
+The fork debugging commands are supported in both native mode and when
+connected to @code{gdbserver} using @kbd{target extended-remote}.
+
 By default, when a program forks, @value{GDBN} will continue to debug
 the parent process and the child process will run unimpeded.
 
@@ -35197,6 +35200,12 @@ The packet indicates that the loaded libraries have changed.
 @value{GDBN} should use @samp{qXfer:libraries:read} to fetch a new
 list of loaded libraries.  The @var{r} part is ignored.
 
+@cindex fork events, remote reply
+@item xfork
+The packet indicates that @code{fork} was called, and @var{r}
+is the ptid of the new child process.  This packet is only
+applicable to targets that support fork events.
+
 @cindex replay log events, remote reply
 @item replaylog
 The packet indicates that the target cannot continue replaying 
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index c710344..8372f36 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -20,6 +20,7 @@
 #include "linux-low.h"
 #include "nat/linux-osdata.h"
 #include "agent.h"
+#include "tdesc.h"
 
 #include "nat/linux-nat.h"
 #include "nat/linux-waitpid.h"
@@ -371,22 +372,23 @@ linux_add_process (int pid, int attached)
 static CORE_ADDR get_pc (struct lwp_info *lwp);
 
 /* Handle a GNU/Linux extended wait response.  If we see a clone
-   event, we need to add the new LWP to our list (and not report the
-   trap to higher layers).  */
+   event, we need to add the new LWP to our list (and return 0 so as
+   not to report the trap to higher layers).  */
 
-static void
-handle_extended_wait (struct lwp_info *event_child, int wstat)
+static int
+handle_extended_wait (struct lwp_info *event_lwp, int wstat)
 {
   int event = linux_ptrace_get_extended_event (wstat);
-  struct thread_info *event_thr = get_lwp_thread (event_child);
+  struct thread_info *event_thr = get_lwp_thread (event_lwp);
   struct lwp_info *new_lwp;
 
-  if (event == PTRACE_EVENT_CLONE)
+  if ((event == PTRACE_EVENT_FORK) || (event == PTRACE_EVENT_CLONE))
     {
       ptid_t ptid;
       unsigned long new_pid;
       int ret, status;
 
+      /* Get the pid of the new lwp.  */
       ptrace (PTRACE_GETEVENTMSG, lwpid_of (event_thr), (PTRACE_TYPE_ARG3) 0,
 	      &new_pid);
 
@@ -406,6 +408,57 @@ handle_extended_wait (struct lwp_info *event_child, int wstat)
 	    warning ("wait returned unexpected status 0x%x", status);
 	}
 
+      if (event == PTRACE_EVENT_FORK)
+	{
+	  struct process_info *parent_proc;
+	  struct process_info *child_proc;
+	  struct lwp_info *child_lwp;
+	  struct target_desc *tdesc;
+
+	  ptid = ptid_build (new_pid, new_pid, 0);
+
+	  if (debug_threads)
+	    {
+	      debug_printf ("HEW: Got fork event from LWP %ld, "
+			    "new child is %d\n",
+			    ptid_get_lwp (ptid_of (event_thr)),
+			    ptid_get_pid (ptid));
+	    }
+
+	  /* Add the new process to the tables and clone the breakpoint
+	     lists of the parent.  We need to do this even if the new process
+	     will be detached, since we will need the process object and the
+	     breakpoints to remove any breakpoints from memory when we
+	     detach, and the host side will access registers.  */
+	  child_proc = linux_add_process (new_pid, 0);
+	  gdb_assert (child_proc != NULL);
+	  child_lwp = add_lwp (ptid);
+	  gdb_assert (child_lwp != NULL);
+	  child_lwp->stopped = 1;
+	  parent_proc = get_thread_process (event_thr);
+	  child_proc->attached = parent_proc->attached;
+	  clone_all_breakpoints (&child_proc->breakpoints,
+				 &child_proc->raw_breakpoints,
+				 parent_proc->breakpoints);
+
+	  tdesc = xmalloc (sizeof (struct target_desc));
+	  copy_target_description (tdesc, parent_proc->tdesc);
+	  child_proc->tdesc = tdesc;
+	  child_lwp->must_set_ptrace_flags = 1;
+
+	  /* Save fork info in the parent thread.  */
+	  event_lwp->waitstatus.kind = TARGET_WAITKIND_FORKED;
+	  event_lwp->waitstatus.value.related_pid = ptid;
+	  /* The status_pending field contains bits denoting the
+	     extended event, so when the pending event is handled,
+	     the handler will look at lwp->waitstatus.  */
+	  event_lwp->status_pending_p = 1;
+	  event_lwp->status_pending = wstat;
+
+	  /* Report the event.  */
+	  return 0;
+	}
+
       if (debug_threads)
 	debug_printf ("HEW: Got clone event "
 		      "from LWP %ld, new child is LWP %ld\n",
@@ -434,7 +487,12 @@ handle_extended_wait (struct lwp_info *event_child, int wstat)
 	  new_lwp->status_pending_p = 1;
 	  new_lwp->status_pending = status;
 	}
+
+      /* Don't report the event.  */
+      return 1;
     }
+
+  internal_error (__FILE__, __LINE__, _("unknown ptrace event %d"), event);
 }
 
 /* Return the PC as read from the regcache of LWP, without any
@@ -1828,6 +1886,19 @@ check_stopped_by_watchpoint (struct lwp_info *child)
   return child->stop_reason == TARGET_STOPPED_BY_WATCHPOINT;
 }
 
+/* Wrapper for linux_enable_event_reporting that disables any
+   supported events that we have determined should not be
+   reported (e.g. GDB did not request them).  */
+
+static void
+linux_low_enable_events (pid_t pid, int attached)
+{
+  if (!report_fork_events)
+    linux_ptrace_clear_flags (PTRACE_O_TRACEFORK);
+
+  linux_enable_event_reporting (pid, attached);
+}
+
 /* Do low-level handling of the event, and check if we should go on
    and pass it to caller code.  Return the affected lwp if we are, or
    NULL otherwise.  */
@@ -1912,11 +1983,11 @@ linux_low_filter_event (int lwpid, int wstat)
 	}
     }
 
-  if (WIFSTOPPED (wstat) && child->must_set_ptrace_flags)
+  if (WIFSTOPPED (wstat) && child->must_set_ptrace_flags && gdb_connected ())
     {
       struct process_info *proc = find_process_pid (pid_of (thread));
 
-      linux_enable_event_reporting (lwpid, proc->attached);
+      linux_low_enable_events (lwpid, proc->attached);
       child->must_set_ptrace_flags = 0;
     }
 
@@ -1926,8 +1997,12 @@ linux_low_filter_event (int lwpid, int wstat)
       && linux_is_extended_waitstatus (wstat))
     {
       child->stop_pc = get_pc (child);
-      handle_extended_wait (child, wstat);
-      return NULL;
+      if (handle_extended_wait (child, wstat))
+	{
+	  /* The event has been handled, so just return without
+	     reporting it.  */
+	  return NULL;
+	}
     }
 
   /* Check first whether this was a SW/HW breakpoint before checking
@@ -2502,6 +2577,18 @@ ignore_event (struct target_waitstatus *ourstatus)
   return null_ptid;
 }
 
+/* Return non-zero if WAITSTATUS reflects an extended linux
+   event.  Otherwise, return zero.  */
+
+static int
+extended_event_reported (const struct target_waitstatus *waitstatus)
+{
+  if (waitstatus == NULL)
+    return 0;
+
+  return (waitstatus->kind == TARGET_WAITKIND_FORKED);
+}
+
 /* Wait for process, returns status.  */
 
 static ptid_t
@@ -2868,7 +2955,8 @@ linux_wait_1 (ptid_t ptid,
 		       && !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);
 
@@ -2890,6 +2978,13 @@ linux_wait_1 (ptid_t ptid,
 			  paddress (event_child->stop_pc),
 			  paddress (event_child->step_range_start),
 			  paddress (event_child->step_range_end));
+	  if (extended_event_reported (&event_child->waitstatus))
+	    {
+	      char *str = target_waitstatus_to_string (ourstatus);
+	      debug_printf ("LWP %ld: extended event 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
@@ -2999,7 +3094,17 @@ linux_wait_1 (ptid_t ptid,
 	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 lwp's waitstatus since we handled it already.  */
+      event_child->waitstatus.kind = TARGET_WAITKIND_SPURIOUS;
+    }
+  else
+    ourstatus->kind = TARGET_WAITKIND_STOPPED;
 
   /* Now that we've selected our final event LWP, un-adjust its PC if
      it was a software breakpoint, and the client doesn't know we can
@@ -3032,7 +3137,7 @@ linux_wait_1 (ptid_t ptid,
 	 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));
     }
@@ -4830,8 +4935,8 @@ linux_write_memory (CORE_ADDR memaddr, const unsigned char *myaddr, int len)
 	val = val & 0xffff;
       else if (len == 3)
 	val = val & 0xffffff;
-      debug_printf ("Writing %0*x to 0x%08lx\n", 2 * ((len < 4) ? len : 4),
-		    val, (long)memaddr);
+      debug_printf ("Writing %0*x to 0x%08lx in process %d\n",
+		    2 * ((len < 4) ? len : 4), val, (long)memaddr, pid);
     }
 
   /* Fill start and end extra bytes of buffer with existing memory data.  */
@@ -5271,6 +5376,39 @@ linux_supports_vfork_events (void)
   return linux_supports_tracefork ();
 }
 
+/* Callback for 'find_inferior'.  Set the (possibly changed) ptrace
+   options for the specified lwp.  */
+
+static int
+reset_lwp_ptrace_options_callback (struct inferior_list_entry *entry,
+				   void *args)
+{
+  struct thread_info *thread = (struct thread_info *) entry;
+  struct lwp_info *lwp = get_thread_lwp (thread);
+  struct process_info *proc = find_process_pid (pid_of (thread));
+
+  linux_low_enable_events (lwpid_of (thread), proc->attached);
+  lwp->must_set_ptrace_flags = 0;
+
+  return 0;
+}
+
+/* Target hook for 'handle_new_gdb_connection'.  Causes a reset of the
+   ptrace flags for all inferiors.  This is in case the new GDB connection
+   doesn't support the same set of events that the previous one did.  */
+
+static void
+linux_handle_new_gdb_connection (void)
+{
+  pid_t pid;
+
+  /* Reset the ptrace options to enable on the inferior(s).  */
+  linux_reset_ptrace_options ();
+
+  /* Request that all the lwps reset their ptrace options.  */
+  find_inferior (&all_threads, reset_lwp_ptrace_options_callback , &pid);
+}
+
 static int
 linux_supports_disable_randomization (void)
 {
@@ -6237,6 +6375,7 @@ static struct target_ops linux_target_ops = {
   linux_supports_multi_process,
   linux_supports_fork_events,
   linux_supports_vfork_events,
+  linux_handle_new_gdb_connection,
 #ifdef USE_THREAD_DB
   thread_db_handle_monitor_command,
 #else
@@ -6315,5 +6454,6 @@ initialize_low (void)
   initialize_low_arch ();
 
   /* Enable extended ptrace events.  */
+  linux_ptrace_set_additional_flags (PTRACE_O_TRACEFORK);
   linux_check_ptrace_features ();
 }
diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h
index bbff3aa..28cccc7 100644
--- a/gdb/gdbserver/linux-low.h
+++ b/gdb/gdbserver/linux-low.h
@@ -270,6 +270,10 @@ struct lwp_info
   /* When stopped is set, the last wait status recorded for this lwp.  */
   int last_status;
 
+  /* This is used to store extended ptrace event information until
+     it is reported to GDB.  */
+  struct target_waitstatus waitstatus;
+
   /* When stopped is set, this is where the lwp last stopped, with
      decr_pc_after_break already accounted for.  If the LWP is
      running, this is the address at which the lwp was resumed.  */
diff --git a/gdb/gdbserver/lynx-low.c b/gdb/gdbserver/lynx-low.c
index 687cce0..38df1b7 100644
--- a/gdb/gdbserver/lynx-low.c
+++ b/gdb/gdbserver/lynx-low.c
@@ -760,6 +760,7 @@ static struct target_ops lynx_target_ops = {
   NULL,  /* supports_multi_process */
   NULL,  /* supports_fork_events */
   NULL,  /* supports_vfork_events */
+  NULL,  /* handle_new_gdb_connection */
   NULL,  /* handle_monitor_command */
 };
 
diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c
index 1de86be..dc43e38 100644
--- a/gdb/gdbserver/remote-utils.c
+++ b/gdb/gdbserver/remote-utils.c
@@ -1114,12 +1114,25 @@ prepare_resume_reply (char *buf, ptid_t ptid,
   switch (status->kind)
     {
     case TARGET_WAITKIND_STOPPED:
+    case TARGET_WAITKIND_FORKED:
       {
 	struct thread_info *saved_thread;
 	const char **regp;
 	struct regcache *regcache;
 
-	sprintf (buf, "T%02x", status->value.sig);
+	if (status->kind == TARGET_WAITKIND_FORKED && report_fork_events)
+	  {
+	    enum gdb_signal signal = GDB_SIGNAL_TRAP;
+	    const char *event = "xfork";
+
+	    sprintf (buf, "T%02x%s:", signal, event);
+	    buf += strlen (buf);
+	    buf = write_ptid (buf, status->value.related_pid);
+	    strcat (buf, ";");
+	  }
+	else
+	  sprintf (buf, "T%02x", status->value.sig);
+
 	buf += strlen (buf);
 
 	saved_thread = current_thread;
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index ee865fa..8fa6f8a 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -2104,6 +2104,9 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
 
       strcat (own_buf, ";vFile:fstat+");
 
+      /* Reinitialize the target as needed for the new connection.  */
+      target_handle_new_gdb_connection ();
+
       return;
     }
 
diff --git a/gdb/gdbserver/server.h b/gdb/gdbserver/server.h
index 91d4080..696a24e 100644
--- a/gdb/gdbserver/server.h
+++ b/gdb/gdbserver/server.h
@@ -84,6 +84,7 @@ extern int disable_packet_qfThreadInfo;
 
 extern int run_once;
 extern int multi_process;
+extern int report_fork_events;
 extern int non_stop;
 
 /* True if the "swbreak+" feature is active.  In that case, GDB wants
diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
index dc51627..ca010ff 100644
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -283,6 +283,9 @@ struct target_ops
   /* Returns true if vfork events are supported.  */
   int (*supports_vfork_events) (void);
 
+  /* Allows target to re-initialize connection-specific settings.  */
+  void (*handle_new_gdb_connection) (void);
+
   /* If not NULL, target-specific routine to process monitor command.
      Returns 1 if handled, or 0 to perform default processing.  */
   int (*handle_monitor_command) (char *);
@@ -422,6 +425,10 @@ int kill_inferior (int);
   (the_target->supports_vfork_events ? \
    (*the_target->supports_vfork_events) () : 0)
 
+#define target_handle_new_gdb_connection() \
+  (the_target->handle_new_gdb_connection ? \
+   (*the_target->handle_new_gdb_connection) () : 0)
+
 #define detach_inferior(pid) \
   (*the_target->detach) (pid)
 
diff --git a/gdb/gdbserver/win32-low.c b/gdb/gdbserver/win32-low.c
index 5f50e46..e7c04d6 100644
--- a/gdb/gdbserver/win32-low.c
+++ b/gdb/gdbserver/win32-low.c
@@ -1829,6 +1829,7 @@ static struct target_ops win32_target_ops = {
   NULL, /* supports_multi_process */
   NULL, /* supports_fork_events */
   NULL, /* supports_vfork_events */
+  NULL, /* handle_new_gdb_connection */
   NULL, /* handle_monitor_command */
   NULL, /* core_of_thread */
   NULL, /* read_loadmap */
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index 22ce842..3f8fe04 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -4898,11 +4898,7 @@ Enables printf debugging output."),
 
   /* Do not enable PTRACE_O_TRACEEXIT until GDB is more prepared to
      support read-only process state.  */
-  linux_ptrace_set_additional_flags (PTRACE_O_TRACESYSGOOD
-				     | PTRACE_O_TRACEVFORKDONE
-				     | PTRACE_O_TRACEVFORK
-				     | PTRACE_O_TRACEFORK
-				     | PTRACE_O_TRACEEXEC);
+  linux_ptrace_set_additional_flags (all_additional_flags);
 }
 \f
 
diff --git a/gdb/nat/linux-ptrace.c b/gdb/nat/linux-ptrace.c
index aba3da8..3494bcf 100644
--- a/gdb/nat/linux-ptrace.c
+++ b/gdb/nat/linux-ptrace.c
@@ -25,13 +25,16 @@
 
 #include <stdint.h>
 
-/* Stores the currently supported ptrace options.  A value of
-   -1 means we did not check for features yet.  A value of 0 means
-   there are no supported features.  */
+/* Stores the ptrace options currently enabled by this gdbserver.
+   A value of -1 means we did not check for features yet.  A value
+   of 0 means there are no enabled features.  */
 static int current_ptrace_options = -1;
 
-/* Additional flags to test.  */
+/* Stores the fork and exec ptrace options supported by the operating
+   system.  */
+static int supported_additional_flags = 0;
 
+/* Additional flags to test.  */
 static int additional_flags;
 
 /* Find all possible reasons we could fail to attach PID and append
@@ -344,6 +347,7 @@ linux_check_ptrace_features (void)
 
   /* Initialize the options.  */
   current_ptrace_options = 0;
+  supported_additional_flags = 0;
 
   /* Fork a child so we can do some testing.  The child will call
      linux_child_function and will get traced.  The child will
@@ -394,7 +398,7 @@ linux_test_for_tracesysgood (int child_pid)
 		(PTRACE_TYPE_ARG4) PTRACE_O_TRACESYSGOOD);
 
   if (ret == 0)
-    current_ptrace_options |= PTRACE_O_TRACESYSGOOD;
+    supported_additional_flags |= PTRACE_O_TRACESYSGOOD;
 }
 
 /* Determine if PTRACE_O_TRACEFORK can be used to follow fork
@@ -421,7 +425,7 @@ linux_test_for_tracefork (int child_pid)
 		    (PTRACE_TYPE_ARG4) (PTRACE_O_TRACEFORK
 					| PTRACE_O_TRACEVFORKDONE));
       if (ret == 0)
-	current_ptrace_options |= PTRACE_O_TRACEVFORKDONE;
+	supported_additional_flags |= PTRACE_O_TRACEVFORKDONE;
     }
 
   /* Setting PTRACE_O_TRACEFORK did not cause an error, however we
@@ -459,9 +463,16 @@ linux_test_for_tracefork (int child_pid)
 	  /* We got the PID from the grandchild, which means fork
 	     tracing is supported.  */
 	  current_ptrace_options |= PTRACE_O_TRACECLONE;
-	  current_ptrace_options |= (additional_flags & (PTRACE_O_TRACEFORK
-                                                         | PTRACE_O_TRACEVFORK
-                                                         | PTRACE_O_TRACEEXEC));
+
+	  /* Save the "extended" options in case we need to reset
+	     the options later for a connect from a different GDB.  */
+	  supported_additional_flags |= (PTRACE_O_TRACEFORK
+					  | PTRACE_O_TRACEVFORK
+					  | PTRACE_O_TRACEEXEC);
+
+	  /* Enable only those options requested by GDB.  */
+	  current_ptrace_options |= (supported_additional_flags
+				     & additional_flags);
 
 	  /* Do some cleanup and kill the grandchild.  */
 	  my_waitpid (second_pid, &second_status, 0);
@@ -518,6 +529,25 @@ linux_enable_event_reporting (pid_t pid, int attached)
 	  (PTRACE_TYPE_ARG4) (uintptr_t) ptrace_options);
 }
 
+/* Reset the ptrace options using potentially different
+   additional_flags.  */
+
+void
+linux_reset_ptrace_options (void)
+{
+  /* Check if we have initialized the ptrace features for this
+     target.  If not, do it now.  */
+  if (current_ptrace_options == -1)
+    linux_check_ptrace_features ();
+
+  /* Clear only the "extended" options.  */
+  linux_ptrace_clear_flags (all_additional_flags);
+
+  /* Add the new requested flags back in.  */
+  current_ptrace_options |= (supported_additional_flags
+			     & additional_flags);
+}
+
 /* Disable reporting of all currently supported ptrace events.  */
 
 void
@@ -603,9 +633,20 @@ linux_ptrace_init_warnings (void)
 void
 linux_ptrace_set_additional_flags (int flags)
 {
+  int invalid_flags = (flags & ~all_additional_flags);
+
+  gdb_assert (invalid_flags == 0);
   additional_flags = flags;
 }
 
+/* Clear FLAGS in current_ptrace_options.  */
+
+void
+linux_ptrace_clear_flags (int flags)
+{
+  current_ptrace_options &= ~flags;
+}
+
 /* Extract extended ptrace event from wait status.  */
 
 int
diff --git a/gdb/nat/linux-ptrace.h b/gdb/nat/linux-ptrace.h
index 03d98c9..c466a19 100644
--- a/gdb/nat/linux-ptrace.h
+++ b/gdb/nat/linux-ptrace.h
@@ -139,6 +139,15 @@ struct buffer;
 # define TRAP_HWBKPT 4
 #endif
 
+/* All of the additional ptrace flags.  The flags passed to
+   linux_ptrace_set_additional_flags must be in
+   this set of flags.  */
+static const int all_additional_flags = (PTRACE_O_TRACEFORK
+					 | PTRACE_O_TRACEVFORK
+					 | PTRACE_O_TRACEVFORKDONE
+					 | PTRACE_O_TRACEEXEC
+					 | PTRACE_O_TRACESYSGOOD);
+
 extern void linux_ptrace_attach_fail_reason (pid_t pid, struct buffer *buffer);
 
 /* Find all possible reasons we could have failed to attach to PTID
@@ -151,12 +160,14 @@ extern char *linux_ptrace_attach_fail_reason_string (ptid_t ptid, int err);
 extern void linux_ptrace_init_warnings (void);
 extern void linux_check_ptrace_features (void);
 extern void linux_enable_event_reporting (pid_t pid, int attached);
+extern void linux_reset_ptrace_options (void);
 extern void linux_disable_event_reporting (pid_t pid);
 extern int linux_supports_tracefork (void);
 extern int linux_supports_traceclone (void);
 extern int linux_supports_tracevforkdone (void);
 extern int linux_supports_tracesysgood (void);
 extern void linux_ptrace_set_additional_flags (int);
+extern void linux_ptrace_clear_flags (int flags);
 extern int linux_ptrace_get_extended_event (int wstat);
 extern int linux_is_extended_waitstatus (int wstat);
 extern int linux_wstatus_maybe_breakpoint (int wstat);
diff --git a/gdb/remote.c b/gdb/remote.c
index 85ced68..899c7a2 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -1470,6 +1470,14 @@ remote_multi_process_p (struct remote_state *rs)
   return packet_support (PACKET_multiprocess_feature) == PACKET_ENABLE;
 }
 
+/* Returns true if fork events are supported.  */
+
+static int
+remote_fork_event_p (struct remote_state *rs)
+{
+  return packet_support (PACKET_fork_event_feature) == PACKET_ENABLE;
+}
+
 /* Tokens for use by the asynchronous signal handlers for SIGINT.  */
 static struct async_signal_handler *async_sigint_remote_twice_token;
 static struct async_signal_handler *async_sigint_remote_token;
@@ -4425,16 +4433,22 @@ remote_open_1 (const char *name, int from_tty,
     wait_forever_enabled_p = 1;
 }
 
-/* This takes a program previously attached to and detaches it.  After
-   this is done, GDB can be used to debug some other program.  We
-   better not have left any breakpoints in the target program or it'll
-   die when it hits one.  */
+/* This detaches a program to which we previously attached, using
+   inferior_ptid to identify the process.  After this is done, GDB
+   can be used to debug some other program.  We better not have left
+   any breakpoints in the target program or it'll die when it hits
+   one.  If IS_FORK_CHILD is true, then inferior_ptid is the child
+   of an unfollowed fork, and we need to avoid deleting breakpoints
+   still needed by the parent.  */
 
 static void
-remote_detach_1 (const char *args, int from_tty, int extended)
+remote_detach_1 (struct target_ops *ops, const char *args,
+		 int from_tty, int is_fork_child)
 {
   int pid = ptid_get_pid (inferior_ptid);
   struct remote_state *rs = get_remote_state ();
+  struct thread_info *tp = find_thread_ptid (inferior_ptid);
+  int is_fork_parent;
 
   if (args)
     error (_("Argument given to \"detach\" when remotely debugging."));
@@ -4468,22 +4482,75 @@ remote_detach_1 (const char *args, int from_tty, int extended)
   else
     error (_("Can't detach process."));
 
-  if (from_tty && !extended)
+  if (from_tty && !rs->extended)
     puts_filtered (_("Ending remote debugging.\n"));
 
-  target_mourn_inferior ();
+  /* Check to see if we are detaching a fork parent.  Note that if we
+     are detaching a fork child, tp == NULL.  */
+  if (tp != NULL)
+    is_fork_parent = tp->pending_follow.kind == TARGET_WAITKIND_FORKED;
+
+  /* If doing detach-on-fork, we don't mourn, because that will delete
+     breakpoints that should be available for the followed inferior.  */
+  if (!is_fork_child && !is_fork_parent)
+    target_mourn_inferior ();
+  else
+    {
+      inferior_ptid = null_ptid;
+      detach_inferior (pid);
+    }
 }
 
 static void
 remote_detach (struct target_ops *ops, const char *args, int from_tty)
 {
-  remote_detach_1 (args, from_tty, 0);
+  remote_detach_1 (ops, args, from_tty, 0);
 }
 
 static void
 extended_remote_detach (struct target_ops *ops, const char *args, int from_tty)
 {
-  remote_detach_1 (args, from_tty, 1);
+  remote_detach_1 (ops, args, from_tty, 0);
+}
+
+/* Target follow-fork function for remote targets.  On entry, and
+   at return, the current inferior is the fork parent.
+
+   Note that although this is currently only used for extended-remote,
+   it is named remote_follow_fork in anticipation of using it for the
+   remote target as well.  */
+
+static int
+remote_follow_fork (struct target_ops *ops, int follow_child,
+		    int detach_fork)
+{
+  struct remote_state *rs = get_remote_state ();
+
+  if (remote_fork_event_p (rs))
+    {
+      if (detach_fork && !follow_child)
+	{
+	  ptid_t parent_ptid;
+	  ptid_t child_ptid;
+
+	  gdb_assert (inferior_thread ()->pending_follow.kind
+		      == TARGET_WAITKIND_FORKED);
+
+	  /* remote_detach_1 detaches inferior_ptid, which is currently
+	     the ptid of the parent.  Switch inferior_ptid to the ptid
+	     of the child for the duration of the call.  */
+	  parent_ptid = inferior_ptid;
+	  child_ptid = inferior_thread ()->pending_follow.value.related_pid;
+	  inferior_ptid = child_ptid;
+	  gdb_assert (!ptid_equal (parent_ptid, child_ptid));
+
+	  remote_detach_1 (ops, NULL, 0, 1);
+
+	  /* Restore the parent ptid.  */
+	  inferior_ptid = parent_ptid;
+	}
+    }
+  return 0;
 }
 
 /* Same as remote_detach, but don't send the "D" packet; just disconnect.  */
@@ -5652,6 +5719,11 @@ Packet: '%s'\n"),
 	      p = unpack_varlen_hex (++p1, &c);
 	      event->core = c;
 	    }
+	  else if (strncmp (p, "xfork", p1 - p) == 0)
+	    {
+	      event->ws.value.related_pid = read_ptid (++p1, &p);
+	      event->ws.kind = TARGET_WAITKIND_FORKED;
+	    }
 	  else
 	    {
 	      ULONGEST pnum;
@@ -9514,8 +9586,11 @@ remote_pid_to_str (struct target_ops *ops, ptid_t ptid)
       if (ptid_equal (magic_null_ptid, ptid))
 	xsnprintf (buf, sizeof buf, "Thread <main>");
       else if (rs->extended && remote_multi_process_p (rs))
-	xsnprintf (buf, sizeof buf, "Thread %d.%ld",
-		   ptid_get_pid (ptid), ptid_get_lwp (ptid));
+	if (ptid_get_lwp (ptid) == 0)
+	  return normal_pid_to_str (ptid);
+	else
+	  xsnprintf (buf, sizeof buf, "Thread %d.%ld",
+		     ptid_get_pid (ptid), ptid_get_lwp (ptid));
       else
 	xsnprintf (buf, sizeof buf, "Thread %ld",
 		   ptid_get_lwp (ptid));
@@ -11938,6 +12013,7 @@ Specify the serial device it is connected to (e.g. /dev/ttya).";
   extended_remote_ops.to_kill = extended_remote_kill;
   extended_remote_ops.to_supports_disable_randomization
     = extended_remote_supports_disable_randomization;
+  extended_remote_ops.to_follow_fork = remote_follow_fork;
 }
 
 static int
diff --git a/gdb/testsuite/gdb.base/multi-forks.exp b/gdb/testsuite/gdb.base/multi-forks.exp
index e95cb4b..2a71e63 100644
--- a/gdb/testsuite/gdb.base/multi-forks.exp
+++ b/gdb/testsuite/gdb.base/multi-forks.exp
@@ -62,6 +62,23 @@ proc continue_to_exit_bp_loc {} {
     set seen_break 0
     set seen_prompt 0
     set seen_timeout 0
+
+    # If we are running with a native gdbserver, the output ($decimal done)
+    # will come via the spawn_id of gdbserver, not the spawn_id of gdb (the
+    # default).  So we grab the spawn_id of gdbserver, if it exists, and
+    # add it to the gdb_expect statement below using "-i", allowing us to
+    # apply the expect statement to the output of both spawn_ids.
+    #
+    # If we are running with a truly remote gdbserver (gdb,noinferiorio),
+    # the output of the program will be inaccessible.  In this case we do
+    # not check for the ($decimal done) output, but just look for the gdb
+    # prompt.
+    global server_spawn_id
+    set current_spawn_id [board_info host fileid]
+    if {![info exists server_spawn_id]} {
+	set server_spawn_id ""
+    }
+
     while { ($seen_done < 16 || ! $seen_prompt) && ! $seen_timeout } {
 	# We don't know what order the interesting things will arrive in.
 	# Using a pattern of the form 'x|y|z' instead of -re x ... -re y
@@ -70,7 +87,8 @@ proc continue_to_exit_bp_loc {} {
 	# first in the script that occurs anywhere in the input, so that
 	# we don't skip anything.
 	gdb_expect {
-	    -re "($decimal done)|(Breakpoint)|($gdb_prompt)" {
+	    -i "$current_spawn_id $server_spawn_id" \
+	         -re "($decimal done)|(Breakpoint)|($gdb_prompt)" {
 		if {[info exists expect_out(1,string)]} {
 		    incr seen_done
 		} elseif {[info exists expect_out(2,string)]} {
@@ -90,7 +108,7 @@ proc continue_to_exit_bp_loc {} {
 	fail "run to exit 2 (no prompt)"
     } elseif { ! $seen_break } {
 	fail "run to exit 2 (no breakpoint hit)"
-    } elseif { $seen_done != 16 } {
+    } elseif {![target_info exists gdb,noinferiorio] && $seen_done != 16 } {
 	fail "run to exit 2 (missing done messages)"
     } else {
 	pass "run to exit 2"
diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index f274b64..2aadd2d 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -29,6 +29,9 @@ load_lib libgloss.exp
 load_lib cache.exp
 load_lib gdb-utils.exp
 
+# Stores the expect spawn ID of gdbserver.
+global server_spawn_id
+
 global GDB
 
 if [info exists TOOL_EXECUTABLE] {
diff --git a/gdb/testsuite/lib/gdbserver-support.exp b/gdb/testsuite/lib/gdbserver-support.exp
index 4a59154..86c6e35 100644
--- a/gdb/testsuite/lib/gdbserver-support.exp
+++ b/gdb/testsuite/lib/gdbserver-support.exp
@@ -270,6 +270,8 @@ proc gdbserver_start { options arguments } {
 	    append gdbserver_command " $arguments"
 	}
 
+	# Save the gdbserver spawn ID.
+	global server_spawn_id
 	set server_spawn_id [remote_spawn target $gdbserver_command]
 
 	# Wait for the server to open its TCP socket, so that GDB can connect.
-- 
1.8.1.1

^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH v6 4/6] Arch-specific remote follow fork
  2015-03-17 20:56     ` [PATCH v6 0/6] Remote fork events Don Breazeal
  2015-03-17 20:56       ` [PATCH v6 1/6] Identify remote fork event support Don Breazeal
  2015-03-17 20:57       ` [PATCH v6 3/6] Extended-remote Linux follow fork Don Breazeal
@ 2015-03-17 20:57       ` Don Breazeal
  2015-03-17 20:57       ` [PATCH v6 2/6] Clone remote breakpoints Don Breazeal
                         ` (2 subsequent siblings)
  5 siblings, 0 replies; 30+ messages in thread
From: Don Breazeal @ 2015-03-17 20:57 UTC (permalink / raw)
  To: gdb-patches, palves

Hi Pedro,

This version of this patch is unchanged except possibly for merges from
the mainline.  It was previously approved here:
https://sourceware.org/ml/gdb-patches/2015-02/msg00262.html

Thanks,
--Don

This patch implements the architecture-specific pieces of follow-fork
for remote and extended-remote Linux targets, which in the current
implementation copyies the parent's debug register state into the new
child's data structures.  This is required for x86, arm, aarch64, and
mips.

This follows the native implementation as closely as possible by
implementing a new linux_target_ops function 'new_fork', which is
analogous to 'linux_nat_new_fork' in linux-nat.c.  In gdbserver, the debug
registers are stored in the process list, instead of an
architecture-specific list, so the function arguments are process_info
pointers instead of an lwp_info and a pid as in the native implementation.

In the MIPS implementation the debug register mirror is stored differently
from x86, ARM, and aarch64, so instead of doing a simple structure assignment
I had to clone the list of watchpoint structures.

Tested using gdb.threads/watchpoint-fork.exp on x86, and ran manual tests
on a MIPS board and an ARM board.

I don't currently have access to an aarch64 board, so if someone is able
to test this easily, please do.

gdb/
2015-03-17  Don Breazeal  <donb@codesourcery.com>

	* NEWS: Remove hardware breakpoint caveat from announcement
	of remote follow fork support.

gdb/gdbserver/
2015-03-17  Don Breazeal  <donb@codesourcery.com>

	* linux-aarch64-low.c (aarch64_linux_new_fork): New function.
	(the_low_target) <new_fork>: Initialize new member.
	* linux-arm-low.c (arm_new_fork): New function.
	(the_low_target) <new_fork>: Initialize new member.
	* linux-low.c (handle_extended_wait): Call new target function
	new_fork.
	* linux-low.h (struct linux_target_ops) <new_fork>: New member.
	* linux-mips-low.c (mips_add_watchpoint): New function
	extracted from mips_insert_point.
	(the_low_target) <new_fork>: Initialize new member.
	(mips_linux_new_fork): New function.
	(mips_insert_point): Call mips_add_watchpoint.
	* linux-x86-low.c (x86_linux_new_fork): New function.
	(the_low_target) <new_fork>: Initialize new member.

---
 gdb/NEWS                          |  3 +-
 gdb/gdbserver/linux-aarch64-low.c | 28 +++++++++++++++
 gdb/gdbserver/linux-arm-low.c     | 42 ++++++++++++++++++++++
 gdb/gdbserver/linux-low.c         |  4 +++
 gdb/gdbserver/linux-low.h         |  3 ++
 gdb/gdbserver/linux-mips-low.c    | 76 ++++++++++++++++++++++++++++++++-------
 gdb/gdbserver/linux-x86-low.c     | 29 +++++++++++++++
 7 files changed, 171 insertions(+), 14 deletions(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index 5aedda5..ce1bb03 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -103,8 +103,7 @@ Itanium running HP-UX         ia64-*-hpux*
 
   GDBserver extended-remote Linux targets now provides basic support
   for fork events.  This enables follow-fork-mode and detach-on-fork
-  for those targets with Linux kernels 2.5.60 and later.  Hardware
-  watchpoints are not inherited across a fork in this implementation.
+  for those targets with Linux kernels 2.5.60 and later.
 
 *** Changes in GDB 7.9
 
diff --git a/gdb/gdbserver/linux-aarch64-low.c b/gdb/gdbserver/linux-aarch64-low.c
index 7934e78..e8fb201 100644
--- a/gdb/gdbserver/linux-aarch64-low.c
+++ b/gdb/gdbserver/linux-aarch64-low.c
@@ -1135,6 +1135,33 @@ aarch64_linux_new_thread (void)
   return info;
 }
 
+static void
+aarch64_linux_new_fork (struct process_info *parent,
+			struct process_info *child)
+{
+  /* These are allocated by linux_add_process.  */
+  gdb_assert (parent->private != NULL
+	      && parent->private->arch_private != NULL);
+  gdb_assert (child->private != NULL
+	      && child->private->arch_private != NULL);
+
+  /* Linux kernel before 2.6.33 commit
+     72f674d203cd230426437cdcf7dd6f681dad8b0d
+     will inherit hardware debug registers from parent
+     on fork/vfork/clone.  Newer Linux kernels create such tasks with
+     zeroed debug registers.
+
+     GDB core assumes the child inherits the watchpoints/hw
+     breakpoints of the parent, and will remove them all from the
+     forked off process.  Copy the debug registers mirrors into the
+     new process so that all breakpoints and watchpoints can be
+     removed together.  The debug registers mirror will become zeroed
+     in the end before detaching the forked off process, thus making
+     this compatible with older Linux kernels too.  */
+
+  *child->private->arch_private = *parent->private->arch_private;
+}
+
 /* Called when resuming a thread.
    If the debug regs have changed, update the thread's copies.  */
 
@@ -1299,6 +1326,7 @@ struct linux_target_ops the_low_target =
   NULL,
   aarch64_linux_new_process,
   aarch64_linux_new_thread,
+  aarch64_linux_new_fork,
   aarch64_linux_prepare_to_resume,
 };
 
diff --git a/gdb/gdbserver/linux-arm-low.c b/gdb/gdbserver/linux-arm-low.c
index 2cd1668..e435ffc 100644
--- a/gdb/gdbserver/linux-arm-low.c
+++ b/gdb/gdbserver/linux-arm-low.c
@@ -717,6 +717,47 @@ arm_new_thread (void)
   return info;
 }
 
+static void
+arm_new_fork (struct process_info *parent, struct process_info *child)
+{
+  struct arch_process_info *parent_proc_info = parent->private->arch_private;
+  struct arch_process_info *child_proc_info = child->private->arch_private;
+  struct lwp_info *child_lwp;
+  struct arch_lwp_info *child_lwp_info;
+  int i;
+
+  /* These are allocated by linux_add_process.  */
+  gdb_assert (parent->private != NULL
+	      && parent->private->arch_private != NULL);
+  gdb_assert (child->private != NULL
+	      && child->private->arch_private != NULL);
+
+  /* Linux kernel before 2.6.33 commit
+     72f674d203cd230426437cdcf7dd6f681dad8b0d
+     will inherit hardware debug registers from parent
+     on fork/vfork/clone.  Newer Linux kernels create such tasks with
+     zeroed debug registers.
+
+     GDB core assumes the child inherits the watchpoints/hw
+     breakpoints of the parent, and will remove them all from the
+     forked off process.  Copy the debug registers mirrors into the
+     new process so that all breakpoints and watchpoints can be
+     removed together.  The debug registers mirror will become zeroed
+     in the end before detaching the forked off process, thus making
+     this compatible with older Linux kernels too.  */
+
+  *child_proc_info = *parent_proc_info;
+
+  /* Mark all the hardware breakpoints and watchpoints as changed to
+     make sure that the registers will be updated.  */
+  child_lwp = find_lwp_pid (ptid_of (child));
+  child_lwp_info = child_lwp->arch_private;
+  for (i = 0; i < MAX_BPTS; i++)
+    child_lwp_info->bpts_changed[i] = 1;
+  for (i = 0; i < MAX_WPTS; i++)
+    child_lwp_info->wpts_changed[i] = 1;
+}
+
 /* Called when resuming a thread.
    If the debug regs have changed, update the thread's copies.  */
 static void
@@ -920,6 +961,7 @@ struct linux_target_ops the_low_target = {
   NULL, /* siginfo_fixup */
   arm_new_process,
   arm_new_thread,
+  arm_new_fork,
   arm_prepare_to_resume,
 };
 
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 8372f36..07dc244 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -446,6 +446,10 @@ handle_extended_wait (struct lwp_info *event_lwp, int wstat)
 	  child_proc->tdesc = tdesc;
 	  child_lwp->must_set_ptrace_flags = 1;
 
+	  /* Clone arch-specific process data.  */
+	  if (the_low_target.new_fork != NULL)
+	    the_low_target.new_fork (parent_proc, child_proc);
+
 	  /* Save fork info in the parent thread.  */
 	  event_lwp->waitstatus.kind = TARGET_WAITKIND_FORKED;
 	  event_lwp->waitstatus.value.related_pid = ptid;
diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h
index 28cccc7..e114a37 100644
--- a/gdb/gdbserver/linux-low.h
+++ b/gdb/gdbserver/linux-low.h
@@ -186,6 +186,9 @@ struct linux_target_ops
      allocate it here.  */
   struct arch_lwp_info * (*new_thread) (void);
 
+  /* Hook to call, if any, when a new fork is attached.  */
+  void (*new_fork) (struct process_info *parent, struct process_info *child);
+
   /* Hook to call prior to resuming a thread.  */
   void (*prepare_to_resume) (struct lwp_info *);
 
diff --git a/gdb/gdbserver/linux-mips-low.c b/gdb/gdbserver/linux-mips-low.c
index f081dda..642e6bd 100644
--- a/gdb/gdbserver/linux-mips-low.c
+++ b/gdb/gdbserver/linux-mips-low.c
@@ -344,6 +344,68 @@ mips_linux_new_thread (void)
   return info;
 }
 
+/* Create a new mips_watchpoint and add it to the list.  */
+
+static void
+mips_add_watchpoint (struct arch_process_info *private, CORE_ADDR addr,
+		     int len, int watch_type)
+{
+  struct mips_watchpoint *new_watch;
+  struct mips_watchpoint **pw;
+
+  new_watch = xmalloc (sizeof (struct mips_watchpoint));
+  new_watch->addr = addr;
+  new_watch->len = len;
+  new_watch->type = watch_type;
+  new_watch->next = NULL;
+
+  pw = &private->current_watches;
+  while (*pw != NULL)
+    pw = &(*pw)->next;
+  *pw = new_watch;
+}
+
+/* Hook to call when a new fork is attached.  */
+
+static void
+mips_linux_new_fork (struct process_info *parent,
+			struct process_info *child)
+{
+  struct arch_process_info *parent_private;
+  struct arch_process_info *child_private;
+  struct mips_watchpoint *wp;
+
+  /* These are allocated by linux_add_process.  */
+  gdb_assert (parent->private != NULL
+	      && parent->private->arch_private != NULL);
+  gdb_assert (child->private != NULL
+	      && child->private->arch_private != NULL);
+
+  /* Linux kernel before 2.6.33 commit
+     72f674d203cd230426437cdcf7dd6f681dad8b0d
+     will inherit hardware debug registers from parent
+     on fork/vfork/clone.  Newer Linux kernels create such tasks with
+     zeroed debug registers.
+
+     GDB core assumes the child inherits the watchpoints/hw
+     breakpoints of the parent, and will remove them all from the
+     forked off process.  Copy the debug registers mirrors into the
+     new process so that all breakpoints and watchpoints can be
+     removed together.  The debug registers mirror will become zeroed
+     in the end before detaching the forked off process, thus making
+     this compatible with older Linux kernels too.  */
+
+  parent_private = parent->private->arch_private;
+  child_private = child->private->arch_private;
+
+  child_private->watch_readback_valid = parent_private->watch_readback_valid;
+  child_private->watch_readback = parent_private->watch_readback;
+
+  for (wp = parent_private->current_watches; wp != NULL; wp = wp->next)
+    mips_add_watchpoint (child_private, wp->addr, wp->len, wp->type);
+
+  child_private->watch_mirror = parent_private->watch_mirror;
+}
 /* This is the implementation of linux_target_ops method
    prepare_to_resume.  If the watch regs have changed, update the
    thread's copies.  */
@@ -397,8 +459,6 @@ mips_insert_point (enum raw_bkpt_type type, CORE_ADDR addr,
   struct process_info *proc = current_process ();
   struct arch_process_info *priv = proc->priv->arch_private;
   struct pt_watch_regs regs;
-  struct mips_watchpoint *new_watch;
-  struct mips_watchpoint **pw;
   int pid;
   long lwpid;
   enum target_hw_bp_type watch_type;
@@ -425,16 +485,7 @@ mips_insert_point (enum raw_bkpt_type type, CORE_ADDR addr,
     return -1;
 
   /* It fit.  Stick it on the end of the list.  */
-  new_watch = xmalloc (sizeof (struct mips_watchpoint));
-  new_watch->addr = addr;
-  new_watch->len = len;
-  new_watch->type = watch_type;
-  new_watch->next = NULL;
-
-  pw = &priv->current_watches;
-  while (*pw != NULL)
-    pw = &(*pw)->next;
-  *pw = new_watch;
+  mips_add_watchpoint (priv, addr, len, watch_type);
 
   priv->watch_mirror = regs;
 
@@ -845,6 +896,7 @@ struct linux_target_ops the_low_target = {
   NULL, /* siginfo_fixup */
   mips_linux_new_process,
   mips_linux_new_thread,
+  mips_linux_new_fork,
   mips_linux_prepare_to_resume
 };
 
diff --git a/gdb/gdbserver/linux-x86-low.c b/gdb/gdbserver/linux-x86-low.c
index 72478d8..5668840 100644
--- a/gdb/gdbserver/linux-x86-low.c
+++ b/gdb/gdbserver/linux-x86-low.c
@@ -781,6 +781,34 @@ x86_linux_new_thread (void)
   return info;
 }
 
+/* Target routine for linux_new_fork.  */
+
+static void
+x86_linux_new_fork (struct process_info *parent, struct process_info *child)
+{
+  /* These are allocated by linux_add_process.  */
+  gdb_assert (parent->priv != NULL
+	      && parent->priv->arch_private != NULL);
+  gdb_assert (child->priv != NULL
+	      && child->priv->arch_private != NULL);
+
+  /* Linux kernel before 2.6.33 commit
+     72f674d203cd230426437cdcf7dd6f681dad8b0d
+     will inherit hardware debug registers from parent
+     on fork/vfork/clone.  Newer Linux kernels create such tasks with
+     zeroed debug registers.
+
+     GDB core assumes the child inherits the watchpoints/hw
+     breakpoints of the parent, and will remove them all from the
+     forked off process.  Copy the debug registers mirrors into the
+     new process so that all breakpoints and watchpoints can be
+     removed together.  The debug registers mirror will become zeroed
+     in the end before detaching the forked off process, thus making
+     this compatible with older Linux kernels too.  */
+
+  *child->priv->arch_private = *parent->priv->arch_private;
+}
+
 /* Called when resuming a thread.
    If the debug regs have changed, update the thread's copies.  */
 
@@ -3441,6 +3469,7 @@ struct linux_target_ops the_low_target =
   x86_siginfo_fixup,
   x86_linux_new_process,
   x86_linux_new_thread,
+  x86_linux_new_fork,
   x86_linux_prepare_to_resume,
   x86_linux_process_qsupported,
   x86_supports_tracepoints,
-- 
1.8.1.1

^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH v6 6/6] Remote fork catch
  2015-03-17 20:56     ` [PATCH v6 0/6] Remote fork events Don Breazeal
                         ` (4 preceding siblings ...)
  2015-03-17 20:58       ` [PATCH v6 5/6] Remote follow vfork Don Breazeal
@ 2015-03-17 20:58       ` Don Breazeal
  2015-03-24 12:47         ` Pedro Alves
  5 siblings, 1 reply; 30+ messages in thread
From: Don Breazeal @ 2015-03-17 20:58 UTC (permalink / raw)
  To: gdb-patches, palves

Hi Pedro,
This patch has been updated per your review comments here:
https://sourceware.org/ml/gdb-patches/2015-03/msg00086.html.
Explanation follows:

On 3/3/2015 7:00 AM, Pedro Alves wrote:
> On 03/03/2015 06:20 AM, Breazeal, Don wrote:
> 
---snip---
>>> Unfortunately, with your full series applied, I get this:
>>>
>>>  (gdb) PASS: gdb.base/disp-step-syscall.exp: vfork: get hexadecimal valueof "$pc"
>>>  stepi
>>>  Detaching from process 29944
>>>  Killing process(es): 29942 29944
>>>  /home/pedro/gdb/mygit/src/gdb/gdbserver/linux-low.c:998: A problem internal to GDBserver has been detected.
>>>  kill_wait_lwp: Assertion `res > 0' failed.
>>>  /home/pedro/gdb/mygit/src/gdb/thread.c:1182: internal-error: switch_to_thread: Assertion `inf != NULL' failed.
>>>  A problem internal to GDB has been detected,
>>>  further debugging may prove unreliable.
>>>  Quit this debugging session? (y or n) FAIL: gdb.base/disp-step-syscall.exp: vfork: stepi vfork insn (GDB internal error)
>>>  Resyncing due to internal error.
---snip---
>
> Seems like gdb disconnects, and we end up in remote_open again.
> Then probably due to --once (the list descriptor is closed), that
> fails and throws, which runs the "kill or detach everything" cleanup
> (detach_or_kill_for_exit_cleanup).  And that ends up in your new
> code here:
> 
> static int
> linux_kill (int pid)
---snip---
> trying to kill the vfork child.
> 
> Really get_last_target_status is not a good idea.  It's broken
> on the native side already, and adding it to gdbserver too is
> not a good idea.  E.g., consider scheduler-locking or non-stop.
> Other events on other processes/threads can easily happen
> and thus overwrite the last target status, before something
> decides to kill the fork parent.

I've eliminated get_last_target_status from gdbserver in this patch.
Instead of killing the new child in gdbserver,
remote.c:extended_remote_kill uses 'pending_follow' on the host side
to recognize a fork parent.  If it finds one it temporarily switches
to the child, kills it, switches back, and resumes the kill operation
as before.

I've got a WIP patch that does something similar in linux-nat.c:linux_nat_kill,
and I've almost got a test working to make sure that the new child is actually
gone.

Thanks
--Don

This patch implements catchpoints for fork events on extended-remote
Linux targets.

Implementation appeared to be straightforward, requiring four new functions
in remote.c to implement insert/remove of fork/vfork catchpoints.  These
functions are essentially stubs that just return 0 ('success') if the
required features are enabled.  If the fork events are being reported, then
catchpoints are set and hit.

However, there are some extra issues that arise with catchpoints.

1) Thread creation reporting -- fork catchpoints are hit before the
   follow_fork has been completed.  When stopped at a fork catchpoint
   in the native implementation, the new process is not 'reported'
   until after the follow is done.  It doesn't show up in the inferiors
   list or the threads list.  However, in the gdbserver case, an
   'info threads' while stopped at a fork catchpoint will retrieve the
   new thread info from the target and add it to GDB's data structures,
   prior to the follow operations.  Because of this premature report,
   things on the GDB side eventually get very confused.

   So in gdbserver, in server.c:handle_qxfer_threads_worker, we check
   'last_status' and if it shows a FORK event, we know that we are in an
   unfollowed fork and we do not report the new (forked) thread to GDB.

2) Kill process before fork is followed -- on the native side in
   linux-nat.c:linux_nat_kill, there is some code to handle the case where
   a fork has occurred but follow_fork hasn't been called yet.  It does
   this by using the last status to determine if a follow is pending, and
   if it is, to kill the child task.  This patch identifies a fork parent
   in remote.c:extended_remote_kill and if one is found, it makes a
   recursive call to kill the new child as well.

3) One of the tests related to fork catchpoints,
   gdb.threads/fork-thread-pending.exp, depended on the threads being
   reported in a specific order.  GDBserver reported the threads in a
   different order, that is, 'info threads' showed the same threads, but in
   a different order.  The test used a hard-coded thread number to find a
   threads that (a) was not the main thread (blocked in pthread_join), and
   (b) was not the forking thread (stopped in fork).

   I implemented a new proc, find_unforked_thread, that uses a pretty
   brute-force method of finding a thread.  I considered just hard-coding
   another number (the native case used thread 2, which was the forking
   thread in the remote case), but that didn't seem future-proof.
   Suggestions on how to do this better would be welcome.
  
Tested on x64 Ubuntu Lucid, native, remote, extended-remote.  Tested the
case of killing the forking process before the fork has been followed
manually.  It wasn't clear to me how to check that a process had actually
been killed from a dejagnu test.

Thanks
--Don

gdb/gdbserver/
2015-03-17  Don Breazeal  <donb@codesourcery.com>

	* server.c (handle_qxfer_threads_worker): Skip forked child thread
	when between fork and follow_fork.
	(get_last_target_status): New function.
	* server.h (get_last_target_status): Declare new function.

gdb/
2015-03-17  Don Breazeal  <donb@codesourcery.com>
	* remote.c (remote_insert_fork_catchpoint): New function.
	(remote_remove_fork_catchpoint): New function.
	(remote_insert_vfork_catchpoint): New function.
	(remote_remove_vfork_catchpoint): New function.
	(extended_remote_kill): Kill fork child when killing the
	parent before follow_fork completes.
	(init_extended_remote_ops): Initialize target vector with
	new fork catchpoint functions.

gdb/testsuite/
2015-03-17  Don Breazeal  <donb@codesourcery.com>
	* gdb.threads/fork-thread-pending.exp (find_unforked_thread):
	New proc.
	(main program): Call find_unforked_thread.

---
 gdb/NEWS                                          |  7 ++-
 gdb/doc/gdb.texinfo                               |  6 +-
 gdb/gdbserver/server.c                            | 18 ++++++
 gdb/gdbserver/server.h                            |  2 +
 gdb/remote.c                                      | 73 +++++++++++++++++++++++
 gdb/testsuite/gdb.threads/fork-thread-pending.exp | 23 ++++++-
 6 files changed, 123 insertions(+), 6 deletions(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index 38f5ed7..a22e5ae 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -104,9 +104,10 @@ Itanium running HP-UX         ia64-*-hpux*
   The remote stub now reports support for fork and vfork events to
   GDB's qSupported query.
 
-  GDBserver extended-remote Linux targets now provides basic support
-  for fork and vfork events.  This enables follow-fork-mode and
-  detach-on-fork for those targets with Linux kernels 2.5.60 and later.
+  GDBserver extended-remote Linux targets now provides support for
+  fork and vfork events.  This enables follow-fork-mode, detach-on-fork,
+  catch fork, and catch vfork for extended-remote targets with Linux
+  kernels 2.5.60 and later.
 
 *** Changes in GDB 7.9
 
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 11e9850..17b86bc 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -4426,12 +4426,14 @@ Again, in this case @value{GDBN} would not be able to display syscall's names.
 @item fork
 @kindex catch fork
 A call to @code{fork}.  This is currently only available for HP-UX
-and @sc{gnu}/Linux.
+and @sc{gnu}/Linux, and when connected to @code{gdbserver} running
+on @sc{gnu}/Linux with @kbd{target extended-remote}.
 
 @item vfork
 @kindex catch vfork
 A call to @code{vfork}.  This is currently only available for HP-UX
-and @sc{gnu}/Linux.
+and @sc{gnu}/Linux, and when connected to @code{gdbserver} running
+on @sc{gnu}/Linux with @kbd{target extended-remote}.
 
 @item load @r{[}regexp@r{]}
 @itemx unload @r{[}regexp@r{]}
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index 8fa6f8a..346f2c4 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -1356,6 +1356,15 @@ handle_qxfer_threads_worker (struct inferior_list_entry *inf, void *arg)
   int core = target_core_of_thread (ptid);
   char core_s[21];
 
+  /* Skip new threads created as the result of a fork if we are not done
+     handling that fork event.  We won't know whether to tell GDB about
+     the new thread until we are done following the fork.  */
+  if ((last_status.kind == TARGET_WAITKIND_FORKED
+       || last_status.kind == TARGET_WAITKIND_VFORKED)
+      && (ptid_get_pid (last_status.value.related_pid)
+	  == ptid_get_pid (ptid)))
+    return;
+
   write_ptid (ptid_s, ptid);
 
   if (core != -1)
@@ -4144,3 +4153,12 @@ handle_target_event (int err, gdb_client_data client_data)
 
   return 0;
 }
+
+/* Retrieve the last waitstatus reported to GDB.  */
+
+void
+get_last_target_status (ptid_t *ptid, struct target_waitstatus *last)
+{
+  *ptid = last_ptid;
+  *last = last_status;
+}
diff --git a/gdb/gdbserver/server.h b/gdb/gdbserver/server.h
index 09a5624..8c6ec27 100644
--- a/gdb/gdbserver/server.h
+++ b/gdb/gdbserver/server.h
@@ -113,6 +113,8 @@ typedef int gdb_fildes_t;
 /* Functions from server.c.  */
 extern int handle_serial_event (int err, gdb_client_data client_data);
 extern int handle_target_event (int err, gdb_client_data client_data);
+extern void get_last_target_status (ptid_t *ptid,
+				    struct target_waitstatus *last);
 
 #include "remote-utils.h"
 
diff --git a/gdb/remote.c b/gdb/remote.c
index d1ba62d..44ee89f 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -1478,6 +1478,54 @@ remote_fork_event_p (struct remote_state *rs)
   return packet_support (PACKET_fork_event_feature) == PACKET_ENABLE;
 }
 
+/* Returns true if vfork events are supported.  */
+
+static int
+remote_vfork_event_p (struct remote_state *rs)
+{
+  return packet_support (PACKET_vfork_event_feature) == PACKET_ENABLE;
+}
+
+/* Insert fork catchpoint target routine.  If fork events are enabled
+   then return success, nothing more to do.  */
+
+static int
+remote_insert_fork_catchpoint (struct target_ops *ops, int pid)
+{
+  struct remote_state *rs = get_remote_state ();
+
+  return !remote_fork_event_p (rs);
+}
+
+/* Remove fork catchpoint target routine.  Nothing to do, just
+   return success.  */
+
+static int
+remote_remove_fork_catchpoint (struct target_ops *ops, int pid)
+{
+  return 0;
+}
+
+/* Insert vfork catchpoint target routine.  If vfork events are enabled
+   then return success, nothing more to do.  */
+
+static int
+remote_insert_vfork_catchpoint (struct target_ops *ops, int pid)
+{
+  struct remote_state *rs = get_remote_state ();
+
+  return !remote_vfork_event_p (rs);
+}
+
+/* Remove vfork catchpoint target routine.  Nothing to do, just
+   return success.  */
+
+static int
+remote_remove_vfork_catchpoint (struct target_ops *ops, int pid)
+{
+  return 0;
+}
+
 /* Tokens for use by the asynchronous signal handlers for SIGINT.  */
 static struct async_signal_handler *async_sigint_remote_twice_token;
 static struct async_signal_handler *async_sigint_remote_token;
@@ -8012,6 +8060,23 @@ extended_remote_kill (struct target_ops *ops)
   int res;
   int pid = ptid_get_pid (inferior_ptid);
   struct remote_state *rs = get_remote_state ();
+  struct thread_info *tp = find_thread_ptid (inferior_ptid);
+
+  /* If we're stopped while forking and we haven't followed yet,
+     kill the child task.  We need to do this first because the
+     parent will be sleeping if this is a vfork.  */
+
+  if (tp != NULL && (tp->pending_follow.kind == TARGET_WAITKIND_FORKED
+		     || tp->pending_follow.kind == TARGET_WAITKIND_VFORKED))
+    {
+      ptid_t parent_ptid = inferior_ptid;
+
+      inferior_ptid = tp->pending_follow.value.related_pid;
+      set_general_thread (inferior_ptid);
+      extended_remote_kill (ops);
+      inferior_ptid = parent_ptid;
+      set_general_thread (inferior_ptid);
+    }
 
   res = remote_vkill (pid, rs);
   if (res == -1 && !(rs->extended && remote_multi_process_p (rs)))
@@ -12036,6 +12101,14 @@ Specify the serial device it is connected to (e.g. /dev/ttya).";
   extended_remote_ops.to_supports_disable_randomization
     = extended_remote_supports_disable_randomization;
   extended_remote_ops.to_follow_fork = remote_follow_fork;
+  extended_remote_ops.to_insert_fork_catchpoint
+    = remote_insert_fork_catchpoint;
+  extended_remote_ops.to_remove_fork_catchpoint
+    = remote_remove_fork_catchpoint;
+  extended_remote_ops.to_insert_vfork_catchpoint
+    = remote_insert_vfork_catchpoint;
+  extended_remote_ops.to_remove_vfork_catchpoint
+    = remote_remove_vfork_catchpoint;
 }
 
 static int
diff --git a/gdb/testsuite/gdb.threads/fork-thread-pending.exp b/gdb/testsuite/gdb.threads/fork-thread-pending.exp
index d229232..594f376 100644
--- a/gdb/testsuite/gdb.threads/fork-thread-pending.exp
+++ b/gdb/testsuite/gdb.threads/fork-thread-pending.exp
@@ -31,6 +31,26 @@ if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executab
     return -1
 }
 
+# Find a thread that did not fork and is not the main thread and
+# return its thread number.  We can't just hard-code the thread
+# number since we have no guarantee as to the ordering of the threads
+# in gdb.  We know that the main thread is in pthread_join and the
+# forking thread is in fork, so we use this rather ungainly regexp
+# to capture an entry from 'info threads' that doesn't show one of
+# those routines, then extract the thread number.
+
+proc find_unforked_thread { } {
+    gdb_test_multiple "info threads" "find unforked thread" {
+        -re "(\[^\r]*Thread\[^\r]* in \[^fp]\[^ot]\[^rh]\[^kr]\[^e]\[^a]\[^d]\[^_]\[^j]\[^\r]*\r\n)" {
+    	    regexp "(\[ 	]*)(\[0-9]*)(\[    ]*Thread\[^\r]*\r\n)" $expect_out(0,string) ignore lead_spc threadnum rest
+        }
+        timeout {
+	    set threadnum -1
+        }
+    }
+    return $threadnum
+}
+
 clean_restart ${binfile}
 
 if ![runto_main] then {
@@ -46,7 +66,8 @@ gdb_test "continue" "Catchpoint.*" "1, get to the fork event"
 
 gdb_test "info threads" " Thread .* Thread .* Thread .* Thread .*" "1, multiple threads found"
 
-gdb_test "thread 1" ".*" "1, switched away from event thread"
+set threadnum [find_unforked_thread]
+gdb_test "thread $threadnum" ".*" "1, switched away from event thread to thread $threadnum"
 
 gdb_test "continue" "Not resuming.*" "1, refused to resume"
 
-- 
1.8.1.1

^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH v6 5/6] Remote follow vfork
  2015-03-17 20:56     ` [PATCH v6 0/6] Remote fork events Don Breazeal
                         ` (3 preceding siblings ...)
  2015-03-17 20:57       ` [PATCH v6 2/6] Clone remote breakpoints Don Breazeal
@ 2015-03-17 20:58       ` Don Breazeal
  2015-03-24 12:28         ` Pedro Alves
  2015-03-17 20:58       ` [PATCH v6 6/6] Remote fork catch Don Breazeal
  5 siblings, 1 reply; 30+ messages in thread
From: Don Breazeal @ 2015-03-17 20:58 UTC (permalink / raw)
  To: gdb-patches, palves

Hi Pedro,

This version of this patch is unchanged except possibly for merges from
the mainline.

Thanks,
--Don

This patch implements follow-fork for vfork on remote and extended-remote
Linux targets.

The implementation follows the native implementation as much as possible.
Most of the work is done on the GDB side in the existing code now in
infrun.c.  GDBserver just has to report the events and do a little
bookkeeping.

Implementation was almost entirely in gdbserver, excepting changes to
gdb/remote.c, and included:

 * enabling VFORK events by adding ptrace options for VFORK and VFORK_DONE
   as 'additional options' in linux-low.c:initialize_low.

 * handling VFORK and VFORK_DONE events in linux-low.c:handle_extended_wait
   and reporting them to GDB.
 
 * including VFORK and VFORK_DONE events in the predicate
   linux-low.c:extended_event_reported.

 * adding support for VFORK and VFORK_DONE events in RSP by adding stop
   reasons "vfork" and "vforkdone" to the 'T' Stop Reply Packet in both
   gdbserver/remote-utils.c and gdb/remote.c.

Tested on x64 Ubuntu Lucid, native, remote, extended-remote.

Thanks
--Don

gdb/doc/
2015-03-17  Don Breazeal  <donb@codesourcery.com>

	* gdb.texinfo (Stop Reply Packets): List new stop reasons
	"vfork" and "vforkdone".

gdb/gdbserver/
2015-03-17  Don Breazeal  <donb@codesourcery.com>

	* linux-low.c (handle_extended_wait): Handle PTRACE_EVENT_FORK and
	PTRACE_EVENT_VFORK_DONE.
	(linux_low_enable_events): Prevent enablement of VFORK events if
	GDB has not requested them.
	(extended_event_reported): Add vfork and vfork-done to the list
	of extended events.
	(initialize_low): Enable PTRACE_EVENT_FORK and PTRACE_EVENT_VFORK_DONE.
	* remote-utils.c (prepare_resume_reply): New stop reasons "vfork"
	and "vforkdone" for RSP 'T' Stop Reply Packet.
	* server.h (report_vfork_events): Declare
	global variable.

gdb/
2015-03-17  Don Breazeal  <donb@codesourcery.com>

	* NEWS: Add vfork and vforkdone to the announcements about fork
	event support and RSP support for fork events.
	* remote.c (remote_follow_fork): Add vfork event type to assert.
	(remote_parse_stop_reply): New stop reasons "vfork" and
	"vforkdone" for RSP 'T' Stop Reply Packet.

---
 gdb/NEWS                     | 11 +++++++----
 gdb/doc/gdb.texinfo          | 14 ++++++++++++++
 gdb/gdbserver/linux-low.c    | 30 +++++++++++++++++++++++++-----
 gdb/gdbserver/remote-utils.c | 17 +++++++++++++++--
 gdb/gdbserver/server.h       |  1 +
 gdb/remote.c                 | 26 ++++++++++++++++++++++++--
 6 files changed, 86 insertions(+), 13 deletions(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index ce1bb03..38f5ed7 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -77,8 +77,11 @@ vFile:fstat:
   Return information about files on the remote system.
 
 T Stop Reply Packet's reason
-  The T stop reply packet supports a new stop reason 'xfork', which
-  signifies that the specified inferior has executed a fork.
+  The T stop reply packet supports new stop reasons 'xfork', 'vfork',
+  and 'vforkdone'.  The 'xfork' and 'vfork' reasons signify that the
+  specified inferior has executed a fork or vfork, respectively. The
+  'vforkdone' reason signifies that the a vforked child process has
+  executed either an exec or exit.
 
 * The info record command now shows the recording format and the
   branch tracing configuration for the current thread when using
@@ -102,8 +105,8 @@ Itanium running HP-UX         ia64-*-hpux*
   GDB's qSupported query.
 
   GDBserver extended-remote Linux targets now provides basic support
-  for fork events.  This enables follow-fork-mode and detach-on-fork
-  for those targets with Linux kernels 2.5.60 and later.
+  for fork and vfork events.  This enables follow-fork-mode and
+  detach-on-fork for those targets with Linux kernels 2.5.60 and later.
 
 *** Changes in GDB 7.9
 
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index b62b37f..11e9850 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -35206,6 +35206,20 @@ The packet indicates that @code{fork} was called, and @var{r}
 is the ptid of the new child process.  This packet is only
 applicable to targets that support fork events.
 
+@cindex vfork events, remote reply
+@item vfork
+The packet indicates that @code{vfork} was called, and @var{r}
+is the ptid of the new child process.  This packet is only
+applicable to targets that support vfork events.
+
+@cindex vforkdone events, remote reply
+@item vforkdone
+The packet indicates that a child process created by a vfork
+has either called @code{exec} or terminated, so that the
+address spaces of the parent and child process are no longer
+shared. The @var{r} part is ignored.  This packet is only
+applicable to targets that support vforkdone events.
+
 @cindex replay log events, remote reply
 @item replaylog
 The packet indicates that the target cannot continue replaying 
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 07dc244..0ca15ec 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -382,7 +382,8 @@ handle_extended_wait (struct lwp_info *event_lwp, int wstat)
   struct thread_info *event_thr = get_lwp_thread (event_lwp);
   struct lwp_info *new_lwp;
 
-  if ((event == PTRACE_EVENT_FORK) || (event == PTRACE_EVENT_CLONE))
+  if ((event == PTRACE_EVENT_FORK) || (event == PTRACE_EVENT_VFORK)
+      || (event == PTRACE_EVENT_CLONE))
     {
       ptid_t ptid;
       unsigned long new_pid;
@@ -408,7 +409,7 @@ handle_extended_wait (struct lwp_info *event_lwp, int wstat)
 	    warning ("wait returned unexpected status 0x%x", status);
 	}
 
-      if (event == PTRACE_EVENT_FORK)
+      if (event == PTRACE_EVENT_FORK || event == PTRACE_EVENT_VFORK)
 	{
 	  struct process_info *parent_proc;
 	  struct process_info *child_proc;
@@ -451,8 +452,13 @@ handle_extended_wait (struct lwp_info *event_lwp, int wstat)
 	    the_low_target.new_fork (parent_proc, child_proc);
 
 	  /* Save fork info in the parent thread.  */
-	  event_lwp->waitstatus.kind = TARGET_WAITKIND_FORKED;
+	  if (event == PTRACE_EVENT_FORK)
+	    event_lwp->waitstatus.kind = TARGET_WAITKIND_FORKED;
+	  else if (event == PTRACE_EVENT_VFORK)
+	    event_lwp->waitstatus.kind = TARGET_WAITKIND_VFORKED;
+
 	  event_lwp->waitstatus.value.related_pid = ptid;
+
 	  /* The status_pending field contains bits denoting the
 	     extended event, so when the pending event is handled,
 	     the handler will look at lwp->waitstatus.  */
@@ -495,6 +501,14 @@ handle_extended_wait (struct lwp_info *event_lwp, int wstat)
       /* Don't report the event.  */
       return 1;
     }
+  else if (event == PTRACE_EVENT_VFORK_DONE)
+    {
+      struct lwp_info *parent_lwp;
+
+      parent_lwp = get_thread_lwp (event_thr);
+      parent_lwp->waitstatus.kind = TARGET_WAITKIND_VFORK_DONE;
+      return 0;
+    }
 
   internal_error (__FILE__, __LINE__, _("unknown ptrace event %d"), event);
 }
@@ -1899,6 +1913,8 @@ linux_low_enable_events (pid_t pid, int attached)
 {
   if (!report_fork_events)
     linux_ptrace_clear_flags (PTRACE_O_TRACEFORK);
+  if (!report_vfork_events)
+    linux_ptrace_clear_flags (PTRACE_O_TRACEVFORK);
 
   linux_enable_event_reporting (pid, attached);
 }
@@ -2590,7 +2606,9 @@ extended_event_reported (const struct target_waitstatus *waitstatus)
   if (waitstatus == NULL)
     return 0;
 
-  return (waitstatus->kind == TARGET_WAITKIND_FORKED);
+  return (waitstatus->kind == TARGET_WAITKIND_FORKED
+	  || waitstatus->kind == TARGET_WAITKIND_VFORKED
+	  || waitstatus->kind == TARGET_WAITKIND_VFORK_DONE);
 }
 
 /* Wait for process, returns status.  */
@@ -6458,6 +6476,8 @@ initialize_low (void)
   initialize_low_arch ();
 
   /* Enable extended ptrace events.  */
-  linux_ptrace_set_additional_flags (PTRACE_O_TRACEFORK);
+  linux_ptrace_set_additional_flags (PTRACE_O_TRACEFORK
+					       | PTRACE_O_TRACEVFORK
+					       | PTRACE_O_TRACEVFORKDONE);
   linux_check_ptrace_features ();
 }
diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c
index dc43e38..42c3ec5 100644
--- a/gdb/gdbserver/remote-utils.c
+++ b/gdb/gdbserver/remote-utils.c
@@ -1115,15 +1115,19 @@ prepare_resume_reply (char *buf, ptid_t ptid,
     {
     case TARGET_WAITKIND_STOPPED:
     case TARGET_WAITKIND_FORKED:
+    case TARGET_WAITKIND_VFORKED:
       {
 	struct thread_info *saved_thread;
 	const char **regp;
 	struct regcache *regcache;
 
-	if (status->kind == TARGET_WAITKIND_FORKED && report_fork_events)
+	if ((status->kind == TARGET_WAITKIND_FORKED && report_fork_events)
+	     || (status->kind == TARGET_WAITKIND_VFORKED
+		 && report_vfork_events))
 	  {
 	    enum gdb_signal signal = GDB_SIGNAL_TRAP;
-	    const char *event = "xfork";
+	    const char *event = (status->kind == TARGET_WAITKIND_FORKED
+				 ? "xfork" : "vfork");
 
 	    sprintf (buf, "T%02x%s:", signal, event);
 	    buf += strlen (buf);
@@ -1245,6 +1249,15 @@ prepare_resume_reply (char *buf, ptid_t ptid,
       else
 	sprintf (buf, "X%02x", status->value.sig);
       break;
+    case TARGET_WAITKIND_VFORK_DONE:
+      if (multi_process)
+	{
+	  enum gdb_signal signal = GDB_SIGNAL_TRAP;
+	  const char *event = "vforkdone";
+
+	  sprintf (buf, "T%02x%s:;", signal, event);
+	}
+      break;
     default:
       error ("unhandled waitkind");
       break;
diff --git a/gdb/gdbserver/server.h b/gdb/gdbserver/server.h
index 696a24e..09a5624 100644
--- a/gdb/gdbserver/server.h
+++ b/gdb/gdbserver/server.h
@@ -85,6 +85,7 @@ extern int disable_packet_qfThreadInfo;
 extern int run_once;
 extern int multi_process;
 extern int report_fork_events;
+extern int report_vfork_events;
 extern int non_stop;
 
 /* True if the "swbreak+" feature is active.  In that case, GDB wants
diff --git a/gdb/remote.c b/gdb/remote.c
index 899c7a2..d1ba62d 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -4526,15 +4526,20 @@ remote_follow_fork (struct target_ops *ops, int follow_child,
 {
   struct remote_state *rs = get_remote_state ();
 
+  /* If fork events aren't supported, then return.  We know that if
+     fork events are supported, then so are vfork events, since they
+     were both introduced in the same version of the linux kernel.  */
   if (remote_fork_event_p (rs))
     {
       if (detach_fork && !follow_child)
 	{
 	  ptid_t parent_ptid;
 	  ptid_t child_ptid;
+	  enum target_waitkind kind;
 
-	  gdb_assert (inferior_thread ()->pending_follow.kind
-		      == TARGET_WAITKIND_FORKED);
+	  kind = inferior_thread ()->pending_follow.kind;
+	  gdb_assert (kind == TARGET_WAITKIND_FORKED
+		      || kind == TARGET_WAITKIND_VFORKED);
 
 	  /* remote_detach_1 detaches inferior_ptid, which is currently
 	     the ptid of the parent.  Switch inferior_ptid to the ptid
@@ -5724,6 +5729,23 @@ Packet: '%s'\n"),
 	      event->ws.value.related_pid = read_ptid (++p1, &p);
 	      event->ws.kind = TARGET_WAITKIND_FORKED;
 	    }
+	  else if (strncmp (p, "vfork", p1 - p) == 0)
+	    {
+	      event->ws.value.related_pid = read_ptid (++p1, &p);
+	      event->ws.kind = TARGET_WAITKIND_VFORKED;
+	    }
+	  else if (strncmp (p, "vforkdone", p1 - p) == 0)
+	    {
+	      char *p_temp;
+
+	      p1++;
+	      p_temp = p1;
+	      while (*p_temp && *p_temp != ';')
+		p_temp++;
+
+	      event->ws.kind = TARGET_WAITKIND_VFORK_DONE;
+	      p = p_temp;
+	    }
 	  else
 	    {
 	      ULONGEST pnum;
-- 
1.8.1.1

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH] Tighten gdb.base/disp-step-syscall.exp
  2015-03-02 20:18     ` [PATCH] Tighten gdb.base/disp-step-syscall.exp (was: Re: [PATCH v5 0/6] Remote fork events) Pedro Alves
  2015-03-03  6:20       ` [PATCH] Tighten gdb.base/disp-step-syscall.exp Breazeal, Don
@ 2015-03-17 21:18       ` Breazeal, Don
  2015-03-18 19:37         ` Pedro Alves
  1 sibling, 1 reply; 30+ messages in thread
From: Breazeal, Don @ 2015-03-17 21:18 UTC (permalink / raw)
  To: Pedro Alves, Breazeal, Don, gdb-patches

On 3/2/2015 12:18 PM, Pedro Alves wrote:
> On 02/27/2015 12:46 AM, Don Breazeal wrote:
>>  - There are a couple of tests that show new failures that actually
>>    fail in the current mainline.  Details of these are as follows:
>>
>>    * when vfork events are enabled, gdb.base/disp-step-syscall.exp
>>      shows PASS => FAIL in .sum diffs.  The test actually always
>>      fails.  With native/master, we see
>>
>>       stepi^M
>>       FAIL: gdb.base/disp-step-syscall.exp: vfork: stepi vfork insn
>> (timeout)
>>
> 
> Hmm, I don't see that here.  I get a full pass on x86_64 Fedora 20.
> Can you try "set debug infrun 1" / "set debug lin-lwp 1" / "set debug displaced 1"
> to check if there's a gdb or kernel bug here?
> 
>>      With remote and extended-remote/master, we see a bogus PASS result:
>>       stepi^M
>>       [Inferior 1 (process 9399) exited normally]^M
>>       (gdb) PASS: gdb.base/disp-step-syscall.exp: vfork: stepi vfork insn
>>
>>     The criteria to pass that test are pretty lax:
>>       gdb_test "stepi" ".*" "stepi $syscall insn"
> 
> Yeah.  I see several other problems.  Here's a patch to improve it.
> 
> Comments?

Hi Pedro,
This patch makes sense to me, and it has been working great for me while
debugging my updates to the follow-fork patchset.  We will need to
update this once the remote follow patches are committed, I guess,
since presumably the kfail 13796 will be resolved then.

> 
> Unfortunately, with your full series applied, I get this:
> 
>  (gdb) PASS: gdb.base/disp-step-syscall.exp: vfork: get hexadecimal valueof "$pc"
>  stepi
>  Detaching from process 29944
>  Killing process(es): 29942 29944
>  /home/pedro/gdb/mygit/src/gdb/gdbserver/linux-low.c:998: A problem internal to GDBserver has been detected.
>  kill_wait_lwp: Assertion `res > 0' failed.
>  /home/pedro/gdb/mygit/src/gdb/thread.c:1182: internal-error: switch_to_thread: Assertion `inf != NULL' failed.
>  A problem internal to GDB has been detected,
>  further debugging may prove unreliable.
>  Quit this debugging session? (y or n) FAIL: gdb.base/disp-step-syscall.exp: vfork: stepi vfork insn (GDB internal error)
>  Resyncing due to internal error.
>  n

The updated patchset just posted should address this issue.
https://sourceware.org/ml/gdb-patches/2015-03/msg00503.html

Thanks,
--Don

> 
> Note, you'll need this one:
> 
>  https://sourceware.org/ml/gdb-patches/2015-03/msg00045.html
> 
> for that internal error to result in a quick bail...
> 
> ----------
> From 1f825812d3f17a2940065d0de38592700e7437bc Mon Sep 17 00:00:00 2001
> From: Pedro Alves <palves@redhat.com>
> Date: Mon, 2 Mar 2015 20:16:23 +0000
> Subject: [PATCH] Tighten gdb.base/disp-step-syscall.exp
> 
> This fixes several problems with this test.
> 
> E.g,. with --target_board=native-extended-gdbserver on
> x86_64 Fedora 20, I get:
> 
>  Running /home/pedro/gdb/mygit/src/gdb/testsuite/gdb.base/disp-step-syscall.exp ...
>  FAIL: gdb.base/disp-step-syscall.exp: vfork: get hexadecimal valueof "$pc" (timeout)
>  FAIL: gdb.base/disp-step-syscall.exp: vfork: single step over vfork final pc
>  FAIL: gdb.base/disp-step-syscall.exp: vfork: delete break vfork insn
>  FAIL: gdb.base/disp-step-syscall.exp: vfork: continue to marker (vfork) (the program is no longer running)
> 
> And with --target=native-gdbserver, I get:
> 
>  Running /home/pedro/gdb/mygit/src/gdb/testsuite/gdb.base/disp-step-syscall.exp ...
>  KPASS: gdb.base/disp-step-syscall.exp: vfork: single step over vfork (PRMS server/13796)
>  FAIL: gdb.base/disp-step-syscall.exp: vfork: get hexadecimal valueof "$pc" (timeout)
>  FAIL: gdb.base/disp-step-syscall.exp: vfork: single step over vfork final pc
>  FAIL: gdb.base/disp-step-syscall.exp: vfork: delete break vfork insn
>  FAIL: gdb.base/disp-step-syscall.exp: vfork: continue to marker (vfork) (the program is no longer running)
> 
> First, the lack of fork support on remote targets is supposed to be
> kfailed, so the KPASS is obviously bogus.  The extended-remote board
> should have KFAILed too.
> 
> The problem is that the test is using "is_remote" instead of
> gdb_is_target_remote.
> 
> And then, I get:
> 
>  (gdb) PASS: gdb.base/disp-step-syscall.exp: vfork: set displaced-stepping on
>  stepi
> 
>  Program terminated with signal SIGSEGV, Segmentation fault.
>  The program no longer exists.
>  (gdb) PASS: gdb.base/disp-step-syscall.exp: vfork: single step over vfork
> 
> Obviously, that should be a FAIL.  The problem is that the test only
> expects SIGILL, not SIGSEGV.  It also doesn't bail correctly if an
> internal error or some other pattern caught by gdb_test_multiple
> matches.  The test doesn't really need to match specific exits/crashes
> patterns, if the PASS regex is improved, like in ...
> 
> ... this and the other "stepi" tests are a bit too lax, passing on
> ".*".  This tightens those up to expect "x/i" and the "=>" current PC
> indicator, like in:
> 
>  1: x/i $pc
>  => 0x3b36abc9e2 <vfork+34>:     syscall
> 
> On x86_64 Fedora 20, I now get a quick KFAIL instead of timeouts with
> both the native-extended-gdbserver and native-gdbserver boards:
> 
>  PASS: gdb.base/disp-step-syscall.exp: vfork: delete break vfork
>  PASS: gdb.base/disp-step-syscall.exp: vfork: continue to syscall insn vfork
>  PASS: gdb.base/disp-step-syscall.exp: vfork: set displaced-stepping on
>  KFAIL: gdb.base/disp-step-syscall.exp: vfork: single step over vfork (PRMS: server/13796)
> 
> and a full pass with native testing.
> 
> gdb/testsuite/
> 2015-03-02  Pedro Alves  <palves@redhat.com>
> 
> 	* gdb.base/disp-step-syscall.exp (disp_step_cross_syscall.exp):
> 	Use gdb_is_target_remote instead of is_remote.  Use
> 	gdb_test_multiple instead of gdb_expect.  Exit early if
> 	gdb_test_multiple hits its internal matches.  Tighten stepi tests
> 	expected output.  Fail on exit with any signal, instead of just
> 	SIGILL.
> ---
>  gdb/testsuite/gdb.base/disp-step-syscall.exp | 53 ++++++++++++++--------------
>  1 file changed, 26 insertions(+), 27 deletions(-)
> 
> diff --git a/gdb/testsuite/gdb.base/disp-step-syscall.exp b/gdb/testsuite/gdb.base/disp-step-syscall.exp
> index ff66f83..b13dce4 100644
> --- a/gdb/testsuite/gdb.base/disp-step-syscall.exp
> +++ b/gdb/testsuite/gdb.base/disp-step-syscall.exp
> @@ -49,6 +49,8 @@ proc disp_step_cross_syscall { syscall } {
>  	    return
>  	}
>  
> +	set is_target_remote [gdb_is_target_remote]
> +
>  	# Delete the breakpoint on main.
>  	gdb_test_no_output "delete break 1"
>  
> @@ -77,27 +79,34 @@ proc disp_step_cross_syscall { syscall } {
>  	gdb_test "display/i \$pc" ".*"
>  
>  
> -	# Single step until we see sysall insn or we reach the upper bound of loop
> -	# iterations.
> -	set see_syscall_insn 0
> -
> -	for {set i 0} {$i < 1000 && $see_syscall_insn == 0} {incr i} {
> -	    send_gdb "stepi\n"
> -	    gdb_expect {
> -		-re ".*$syscall_insn.*$gdb_prompt $" {
> -		    set see_syscall_insn 1
> +	# Single step until we see a syscall insn or we reach the
> +	# upper bound of loop iterations.
> +	set msg "find syscall insn in $syscall"
> +	set steps 0
> +	set max_steps 1000
> +	gdb_test_multiple "stepi" $msg {
> +	    -re ".*$syscall_insn.*$gdb_prompt $" {
> +		pass $msg
> +	    }
> +	    -re "x/i .*=>.*\r\n$gdb_prompt $" {
> +		incr steps
> +		if {$steps == $max_steps} {
> +		    fail $msg
> +		} else {
> +		    send_gdb "stepi\n"
> +		    exp_continue
>  		}
> -		-re ".*$gdb_prompt $" {}
>  	    }
>  	}
>  
> -	if {$see_syscall_insn == 0} then {
> -	    fail "find syscall insn in $syscall"
> +	if {$steps == $max_steps} {
>  	    return -1
>  	}
>  
>  	set syscall_insn_addr [get_hexadecimal_valueof "\$pc" "0"]
> -	gdb_test "stepi" ".*" "stepi $syscall insn"
> +	if {[gdb_test "stepi" "x/i .*=>.*" "stepi $syscall insn"] != 0} {
> +	    return -1
> +	}
>  	set syscall_insn_next_addr [get_hexadecimal_valueof "\$pc" "0"]
>  
>  	gdb_test "continue" "Continuing\\..*Breakpoint \[0-9\]+, (.* in |__libc_|)$syscall \\(\\).*" \
> @@ -121,22 +130,12 @@ proc disp_step_cross_syscall { syscall } {
>  	gdb_test_no_output "set displaced-stepping on"
>  
>  	# Check the address of next instruction of syscall.
> -	if {$syscall == "vfork" && [is_remote target]} {
> +	if {$syscall == "vfork" && $is_target_remote} {
>  	    setup_kfail server/13796 "*-*-*"
>  	}
> -	set test "single step over $syscall"
> -	gdb_test_multiple "stepi" $test {
> -	    -re "Program terminated with signal SIGILL,.*\r\n$gdb_prompt $" {
> -		fail $test
> -		return
> -	    }
> -	    -re "\\\[Inferior .* exited normally\\\].*\r\n$gdb_prompt $" {
> -		fail $test
> -		return
> -	    }
> -	    -re "\r\n$gdb_prompt $" {
> -		pass $test
> -	    }
> +
> +	if {[gdb_test "stepi" "x/i .*=>.*" "single step over $syscall"] != 0} {
> +	    return -1
>  	}
>  
>  	set syscall_insn_next_addr_found [get_hexadecimal_valueof "\$pc" "0"]
> 

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH] Tighten gdb.base/disp-step-syscall.exp
  2015-03-17 21:18       ` Breazeal, Don
@ 2015-03-18 19:37         ` Pedro Alves
  0 siblings, 0 replies; 30+ messages in thread
From: Pedro Alves @ 2015-03-18 19:37 UTC (permalink / raw)
  To: Breazeal, Don, Breazeal, Don, gdb-patches

Hi Don,

On 03/17/2015 09:18 PM, Breazeal, Don wrote:

> Hi Pedro,
> This patch makes sense to me, and it has been working great for me while
> debugging my updates to the follow-fork patchset.  We will need to
> update this once the remote follow patches are committed, I guess,
> since presumably the kfail 13796 will be resolved then.

Alright, might as well push it in then.  I've done that now.

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v6 1/6] Identify remote fork event support
  2015-03-17 20:56       ` [PATCH v6 1/6] Identify remote fork event support Don Breazeal
@ 2015-03-24 10:57         ` Pedro Alves
  2015-03-26 19:00           ` [PATCH v5 01/06] " Don Breazeal
  0 siblings, 1 reply; 30+ messages in thread
From: Pedro Alves @ 2015-03-24 10:57 UTC (permalink / raw)
  To: Don Breazeal, gdb-patches

On 03/17/2015 08:56 PM, Don Breazeal wrote:

> @@ -12508,7 +12524,8 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
>    add_packet_config_cmd (&remote_protocol_packets[PACKET_hwbreak_feature],
>                           "hwbreak-feature", "hwbreak-feature", 0);
>  
> -  /* Assert that we've registered commands for all packet configs.  */
> +  /* Assert that we've registered "set remote foo-packet" commands
> +     for all packet configs.  */
>    {
>      int i;
>  

This hunk could go immediately/separately as obvious.


> @@ -12527,6 +12544,8 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
>  	  case PACKET_DisconnectedTracing_feature:
>  	  case PACKET_augmented_libraries_svr4_read_feature:
>  	  case PACKET_qCRC:
> +	  case PACKET_fork_event_feature:
> +	  case PACKET_vfork_event_feature:
>  	    /* Additions to this list need to be well justified:
>  	       pre-existing packets are OK; new packets are not.  */

I think I mentioned this before: please do register commands for these
features.  As the comment says, new packets here are not OK without
good justification, and I can't think of a good justification.
Then you'll need to make sure remote_query_supported only includes the
feature in GDB's query if the feature wasn't force-disabled.  See
the new swbreak+ feature's handling in that function.

>  	    excepted = 1;

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v6 3/6] Extended-remote Linux follow fork
  2015-03-17 20:57       ` [PATCH v6 3/6] Extended-remote Linux follow fork Don Breazeal
@ 2015-03-24 12:17         ` Pedro Alves
  2015-04-07 17:35           ` Pedro Alves
  0 siblings, 1 reply; 30+ messages in thread
From: Pedro Alves @ 2015-03-24 12:17 UTC (permalink / raw)
  To: Don Breazeal, gdb-patches

Hi Don,

On 03/17/2015 08:56 PM, Don Breazeal wrote:

>  * using a new predicate in gdbserver to control handling of the fork event
>    (and eventually all extended events) in linux_wait_1.  The predicate,
>    extended_event_reported, checks a target_waitstatus.kind for an
>    extended ptrace event.
> 
>  * implementing a new RSP 'T' Stop Reply Packet stop reason: "fork", in
>    gdbserver/remote-utils.c and remote.c.
> 
>  * implementing new target and RSP support for target_follow_fork with
>    target extended-remote.  (The RSP components were actually defined in
>    patch 4, but they see their first use here).

This description seems to be a bit out of date.  We're in patch 3 still.  :-)

> 
>    - remote target routine remote_follow_fork, which just sends the 'D;pid'
>      detach packet to detach the new fork child cleanly.  We can't just
>      call target_detach because the data structures for the forked child
>      have not been allocated on the host side.
> 
> Tested on x64 Ubuntu Lucid, native, remote, extended-remote.
> 
> gdb/doc/
> 2015-03-17  Don Breazeal  <donb@codesourcery.com>
> 
> 	* gdb.texinfo (Forks): List targets that have fork debugging
> 	support.
> 	(Stop Reply Packets): List new stop reason "xfork".

I know I was the one who suggested avoiding "fork" because it starts
with an hex char, but thinking further, if gdb must report
support for fork events for the server to report these, it's ok
to use "fork".  I'm no thinking we can rename it back, thus
fending off questions about this in the future.  Could you
do that?  Sorry about the back and forth...

>  
> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index b089b1b..b62b37f 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -3142,6 +3142,9 @@ create additional processes using the @code{fork} or @code{vfork} functions.
>  Currently, the only platforms with this feature are HP-UX (11.x and later
>  only?) and @sc{gnu}/Linux (kernel version 2.5.60 and later).
>  
> +The fork debugging commands are supported in both native mode and when
> +connected to @code{gdbserver} using @kbd{target extended-remote}.
> +
>  By default, when a program forks, @value{GDBN} will continue to debug
>  the parent process and the child process will run unimpeded.
>  
> @@ -35197,6 +35200,12 @@ The packet indicates that the loaded libraries have changed.
>  @value{GDBN} should use @samp{qXfer:libraries:read} to fetch a new
>  list of loaded libraries.  The @var{r} part is ignored.
>  
> +@cindex fork events, remote reply
> +@item xfork
> +The packet indicates that @code{fork} was called, and @var{r}
> +is the ptid of the new child process.  This packet is only
> +applicable to targets that support fork events.
> +

Please mention here the need for reporting support in qSupported.
E.g., the new swbreak stop reason says:

~~~
This packet should not be sent by default; older @value{GDBN} versions
did not support it.  @value{GDBN} requests it, by supplying an
appropriate @samp{qSupported} feature (@pxref{qSupported}).  The
remote stub must also supply the appropriate @samp{qSupported} feature
indicating support.
~~~

>        if (debug_threads)
>  	debug_printf ("HEW: Got clone event "
>  		      "from LWP %ld, new child is LWP %ld\n",
> @@ -434,7 +487,12 @@ handle_extended_wait (struct lwp_info *event_child, int wstat)
>  	  new_lwp->status_pending_p = 1;
>  	  new_lwp->status_pending = status;
>  	}
> +
> +      /* Don't report the event.  */
> +      return 1;
>      }
> +
> +  internal_error (__FILE__, __LINE__, _("unknown ptrace event %d"), event);
>  }
>  
>  /* Return the PC as read from the regcache of LWP, without any
> @@ -1828,6 +1886,19 @@ check_stopped_by_watchpoint (struct lwp_info *child)
>    return child->stop_reason == TARGET_STOPPED_BY_WATCHPOINT;
>  }
>  
> +/* Wrapper for linux_enable_event_reporting that disables any
> +   supported events that we have determined should not be
> +   reported (e.g. GDB did not request them).  */
> +
> +static void
> +linux_low_enable_events (pid_t pid, int attached)
> +{
> +  if (!report_fork_events)
> +    linux_ptrace_clear_flags (PTRACE_O_TRACEFORK);
> +
> +  linux_enable_event_reporting (pid, attached);
> +}
> +
>  /* Do low-level handling of the event, and check if we should go on
>     and pass it to caller code.  Return the affected lwp if we are, or
>     NULL otherwise.  */
> @@ -1912,11 +1983,11 @@ linux_low_filter_event (int lwpid, int wstat)
>  	}
>      }
>  
> -  if (WIFSTOPPED (wstat) && child->must_set_ptrace_flags)
> +  if (WIFSTOPPED (wstat) && child->must_set_ptrace_flags && gdb_connected ())
>      {

I don't really understand this.  If the flag is set, why would it matter
whether gdb is connected?

>        struct process_info *proc = find_process_pid (pid_of (thread));
>  
> -      linux_enable_event_reporting (lwpid, proc->attached);
> +      linux_low_enable_events (lwpid, proc->attached);
>        child->must_set_ptrace_flags = 0;
>      }
>  
> @@ -1926,8 +1997,12 @@ linux_low_filter_event (int lwpid, int wstat)
>        && linux_is_extended_waitstatus (wstat))
>      {
>        child->stop_pc = get_pc (child);
> -      handle_extended_wait (child, wstat);
> -      return NULL;
> +      if (handle_extended_wait (child, wstat))
> +	{
> +	  /* The event has been handled, so just return without
> +	     reporting it.  */
> +	  return NULL;
> +	}
>      }
>  
>    /* Check first whether this was a SW/HW breakpoint before checking
> @@ -2502,6 +2577,18 @@ ignore_event (struct target_waitstatus *ourstatus)
>    return null_ptid;
>  }
>  
> +/* Return non-zero if WAITSTATUS reflects an extended linux
> +   event.  Otherwise, return zero.  */
> +
> +static int
> +extended_event_reported (const struct target_waitstatus *waitstatus)
> +{
> +  if (waitstatus == NULL)
> +    return 0;
> +
> +  return (waitstatus->kind == TARGET_WAITKIND_FORKED);
> +}
> +
>  /* Wait for process, returns status.  */
>  
>  static ptid_t
> @@ -2868,7 +2955,8 @@ linux_wait_1 (ptid_t ptid,
>  		       && !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);
>  
> @@ -2890,6 +2978,13 @@ linux_wait_1 (ptid_t ptid,
>  			  paddress (event_child->stop_pc),
>  			  paddress (event_child->step_range_start),
>  			  paddress (event_child->step_range_end));
> +	  if (extended_event_reported (&event_child->waitstatus))
> +	    {
> +	      char *str = target_waitstatus_to_string (ourstatus);
> +	      debug_printf ("LWP %ld: extended event 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
> @@ -2999,7 +3094,17 @@ linux_wait_1 (ptid_t ptid,
>  	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 lwp's waitstatus since we handled it already.  */
> +      event_child->waitstatus.kind = TARGET_WAITKIND_SPURIOUS;

I'd think this should be TARGET_WAITKIND_IGNORE?  That's the usual
status for "clear".

> +    }
> +  else
> +    ourstatus->kind = TARGET_WAITKIND_STOPPED;
>  
>    /* Now that we've selected our final event LWP, un-adjust its PC if
>       it was a software breakpoint, and the client doesn't know we can
> @@ -3032,7 +3137,7 @@ linux_wait_1 (ptid_t ptid,
>  	 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));
>      }
> @@ -4830,8 +4935,8 @@ linux_write_memory (CORE_ADDR memaddr, const unsigned char *myaddr, int len)
>  	val = val & 0xffff;
>        else if (len == 3)
>  	val = val & 0xffffff;
> -      debug_printf ("Writing %0*x to 0x%08lx\n", 2 * ((len < 4) ? len : 4),
> -		    val, (long)memaddr);
> +      debug_printf ("Writing %0*x to 0x%08lx in process %d\n",
> +		    2 * ((len < 4) ? len : 4), val, (long)memaddr, pid);
>      }
>  
>    /* Fill start and end extra bytes of buffer with existing memory data.  */
> @@ -5271,6 +5376,39 @@ linux_supports_vfork_events (void)
>    return linux_supports_tracefork ();
>  }
>  
> +/* Callback for 'find_inferior'.  Set the (possibly changed) ptrace
> +   options for the specified lwp.  */
> +
> +static int
> +reset_lwp_ptrace_options_callback (struct inferior_list_entry *entry,
> +				   void *args)
> +{
> +  struct thread_info *thread = (struct thread_info *) entry;
> +  struct lwp_info *lwp = get_thread_lwp (thread);
> +  struct process_info *proc = find_process_pid (pid_of (thread));
> +
> +  linux_low_enable_events (lwpid_of (thread), proc->attached);
> +  lwp->must_set_ptrace_flags = 0;
> +
> +  return 0;
> +}
> +
> +/* Target hook for 'handle_new_gdb_connection'.  Causes a reset of the
> +   ptrace flags for all inferiors.  This is in case the new GDB connection
> +   doesn't support the same set of events that the previous one did.  */
> +
> +static void
> +linux_handle_new_gdb_connection (void)
> +{
> +  pid_t pid;
> +
> +  /* Reset the ptrace options to enable on the inferior(s).  */
> +  linux_reset_ptrace_options ();
> +
> +  /* Request that all the lwps reset their ptrace options.  */
> +  find_inferior (&all_threads, reset_lwp_ptrace_options_callback , &pid);

Spurious space before ', &pid'.  But, you can't do this if threads
are already running, such as when you reconnect after you left the
target running for disconnected tracing.  Instead, you need to
force threads to momentarily pause and set their must_set_ptrace_flags
flag, much like when we need to change running thread's debug registers
for watchpoints.  See linux-x86-low.c:update_debug_registers_callback.

>  
> diff --git a/gdb/nat/linux-ptrace.c b/gdb/nat/linux-ptrace.c
> index aba3da8..3494bcf 100644
> --- a/gdb/nat/linux-ptrace.c
> +++ b/gdb/nat/linux-ptrace.c
> @@ -25,13 +25,16 @@
>  
>  #include <stdint.h>
>  
> -/* Stores the currently supported ptrace options.  A value of
> -   -1 means we did not check for features yet.  A value of 0 means
> -   there are no supported features.  */
> +/* Stores the ptrace options currently enabled by this gdbserver.

Talking about "this gdbserver" here doesn't make sense.  This file
is used by native gdb too.

> +   A value of -1 means we did not check for features yet.  A value
> +   of 0 means there are no enabled features.  */
>  static int current_ptrace_options = -1;
>  
> -/* Additional flags to test.  */
> +/* Stores the fork and exec ptrace options supported by the operating
> +   system.  */
> +static int supported_additional_flags = 0;
>  
> +/* Additional flags to test.  */
>  static int additional_flags;
>  
>  /* Find all possible reasons we could fail to attach PID and append
> @@ -344,6 +347,7 @@ linux_check_ptrace_features (void)
>  
>    /* Initialize the options.  */
>    current_ptrace_options = 0;
> +  supported_additional_flags = 0;
>  
>    /* Fork a child so we can do some testing.  The child will call
>       linux_child_function and will get traced.  The child will
> @@ -394,7 +398,7 @@ linux_test_for_tracesysgood (int child_pid)
>  		(PTRACE_TYPE_ARG4) PTRACE_O_TRACESYSGOOD);
>  
>    if (ret == 0)
> -    current_ptrace_options |= PTRACE_O_TRACESYSGOOD;
> +    supported_additional_flags |= PTRACE_O_TRACESYSGOOD;
>  }
>  
>  /* Determine if PTRACE_O_TRACEFORK can be used to follow fork
> @@ -421,7 +425,7 @@ linux_test_for_tracefork (int child_pid)
>  		    (PTRACE_TYPE_ARG4) (PTRACE_O_TRACEFORK
>  					| PTRACE_O_TRACEVFORKDONE));
>        if (ret == 0)
> -	current_ptrace_options |= PTRACE_O_TRACEVFORKDONE;
> +	supported_additional_flags |= PTRACE_O_TRACEVFORKDONE;
>      }
>  
>    /* Setting PTRACE_O_TRACEFORK did not cause an error, however we
> @@ -459,9 +463,16 @@ linux_test_for_tracefork (int child_pid)
>  	  /* We got the PID from the grandchild, which means fork
>  	     tracing is supported.  */
>  	  current_ptrace_options |= PTRACE_O_TRACECLONE;
> -	  current_ptrace_options |= (additional_flags & (PTRACE_O_TRACEFORK
> -                                                         | PTRACE_O_TRACEVFORK
> -                                                         | PTRACE_O_TRACEEXEC));
> +
> +	  /* Save the "extended" options in case we need to reset
> +	     the options later for a connect from a different GDB.  */
> +	  supported_additional_flags |= (PTRACE_O_TRACEFORK
> +					  | PTRACE_O_TRACEVFORK
> +					  | PTRACE_O_TRACEEXEC);
> +
> +	  /* Enable only those options requested by GDB.  */
> +	  current_ptrace_options |= (supported_additional_flags
> +				     & additional_flags);
>  
>  	  /* Do some cleanup and kill the grandchild.  */
>  	  my_waitpid (second_pid, &second_status, 0);
> @@ -518,6 +529,25 @@ linux_enable_event_reporting (pid_t pid, int attached)
>  	  (PTRACE_TYPE_ARG4) (uintptr_t) ptrace_options);
>  }
>  
> +/* Reset the ptrace options using potentially different
> +   additional_flags.  */
> +
> +void
> +linux_reset_ptrace_options (void)
> +{
> +  /* Check if we have initialized the ptrace features for this
> +     target.  If not, do it now.  */
> +  if (current_ptrace_options == -1)
> +    linux_check_ptrace_features ();
> +
> +  /* Clear only the "extended" options.  */
> +  linux_ptrace_clear_flags (all_additional_flags);
> +
> +  /* Add the new requested flags back in.  */
> +  current_ptrace_options |= (supported_additional_flags
> +			     & additional_flags);
> +}
> +
>  /* Disable reporting of all currently supported ptrace events.  */
>  
>  void
> @@ -603,9 +633,20 @@ linux_ptrace_init_warnings (void)
>  void
>  linux_ptrace_set_additional_flags (int flags)
>  {
> +  int invalid_flags = (flags & ~all_additional_flags);
> +
> +  gdb_assert (invalid_flags == 0);
>    additional_flags = flags;
>  }
>  
> +/* Clear FLAGS in current_ptrace_options.  */
> +
> +void
> +linux_ptrace_clear_flags (int flags)
> +{
> +  current_ptrace_options &= ~flags;
> +}
> +

This still looks a bit messy and mostly unnecessary to me.  :-/

Why not something based on this?  In linux-ptrace.c:

/* Enable reporting of all currently supported ptrace events.
   OPTIONS is a bit mask of extended features we want enabled,
   if supported by the kernel.  PTRACE_O_TRACECLONE is always
   enabled, if supported.  */

void
linux_enable_event_reporting (pid_t pid, int options)
{
  /* Check if we have initialized the ptrace features for this
     target.  If not, do it now.  */
  if (current_ptrace_options == -1)
    linux_check_ptrace_features ();

  /* We always want clone events.  */
  options |= PTRACE_O_TRACECLONE;

  /* Filter out unsupported options.  */
  options &= current_ptrace_options;

  /* Set the options.  */
  ptrace (PTRACE_SETOPTIONS, pid, (PTRACE_TYPE_ARG3) 0,
	  (PTRACE_TYPE_ARG4) (uintptr_t) options);
}


(current_ptrace_options would be renamed to
supported_ptrace_options or some such afterwards.)

and then in linux-low.c:

  if (WIFSTOPPED (wstat) && child->must_set_ptrace_flags)
    {
      struct process_info *proc = find_process_pid (pid_of (thread));
      int options = 0;

      if (!proc->attached)
        options |= PTRACE_O_EXITKILL;

      if (report_fork_events)
	options |= PTRACE_O_TRACEFORK;

      if (report_vfork_events)
	options |= (PTRACE_O_TRACEVFORKDONE | PTRACE_O_TRACEVFORK);

      if (report_exec_events)
	options |= PTRACE_O_TRACEEXEC;

      linux_enable_event_reporting (pid, options);
      child->must_set_ptrace_flags = 0;
    }

That'd cope with a different gdb reconnecting and requesting
different options too.


> -/* This takes a program previously attached to and detaches it.  After
> -   this is done, GDB can be used to debug some other program.  We
> -   better not have left any breakpoints in the target program or it'll
> -   die when it hits one.  */
> +/* This detaches a program to which we previously attached, using
> +   inferior_ptid to identify the process.  After this is done, GDB
> +   can be used to debug some other program.  We better not have left
> +   any breakpoints in the target program or it'll die when it hits
> +   one.  If IS_FORK_CHILD is true, then inferior_ptid is the child
> +   of an unfollowed fork, and we need to avoid deleting breakpoints
> +   still needed by the parent.  */
>  
>  static void
> -remote_detach_1 (const char *args, int from_tty, int extended)
> +remote_detach_1 (struct target_ops *ops, const char *args,
> +		 int from_tty, int is_fork_child)
>  {
>    int pid = ptid_get_pid (inferior_ptid);
>    struct remote_state *rs = get_remote_state ();
> +  struct thread_info *tp = find_thread_ptid (inferior_ptid);
> +  int is_fork_parent;
>  
>    if (args)
>      error (_("Argument given to \"detach\" when remotely debugging."));
> @@ -4468,22 +4482,75 @@ remote_detach_1 (const char *args, int from_tty, int extended)
>    else
>      error (_("Can't detach process."));
>  
> -  if (from_tty && !extended)
> +  if (from_tty && !rs->extended)
>      puts_filtered (_("Ending remote debugging.\n"));
>  
> -  target_mourn_inferior ();
> +  /* Check to see if we are detaching a fork parent.  Note that if we
> +     are detaching a fork child, tp == NULL.  */
> +  if (tp != NULL)
> +    is_fork_parent = tp->pending_follow.kind == TARGET_WAITKIND_FORKED;
> +
> +  /* If doing detach-on-fork, we don't mourn, because that will delete
> +     breakpoints that should be available for the followed inferior.  */
> +  if (!is_fork_child && !is_fork_parent)
> +    target_mourn_inferior ();
> +  else
> +    {
> +      inferior_ptid = null_ptid;
> +      detach_inferior (pid);
> +    }
>  }

This looks quite confusing to me.  Please instead simply factor out
the bits that actually do the remote detach to a separate
function, like:

static void
remote_detach_pid (int pid)
{
  struct remote_state *rs = get_remote_state ();

  if (remote_multi_process_p (rs))
    xsnprintf (rs->buf, get_remote_packet_size (), "D;%x", pid);
  else
    strcpy (rs->buf, "D");

  putpkt (rs->buf);
  getpkt (&rs->buf, &rs->buf_size, 0);

  if (rs->buf[0] == 'O' && rs->buf[1] == 'K')
    ;
  else if (rs->buf[0] == '\0')
    error (_("Remote doesn't know how to detach"));
  else
    error (_("Can't detach process."));
}

And use that from both remote_detach_1 and the detach-child-fork
case.

> +static int
> +remote_follow_fork (struct target_ops *ops, int follow_child,
> +		    int detach_fork)
> +{
> +  struct remote_state *rs = get_remote_state ();
> +
> +  if (remote_fork_event_p (rs))
> +    {
> +      if (detach_fork && !follow_child)

Aren't we missing the "detach_fork && follow_child" case?

> +	{
> +	  ptid_t parent_ptid;
> +	  ptid_t child_ptid;
> +
> +	  gdb_assert (inferior_thread ()->pending_follow.kind
> +		      == TARGET_WAITKIND_FORKED);
> +
> +	  /* remote_detach_1 detaches inferior_ptid, which is currently
> +	     the ptid of the parent.  Switch inferior_ptid to the ptid
> +	     of the child for the duration of the call.  */
> +	  parent_ptid = inferior_ptid;
> +	  child_ptid = inferior_thread ()->pending_follow.value.related_pid;
> +	  inferior_ptid = child_ptid;
> +	  gdb_assert (!ptid_equal (parent_ptid, child_ptid));
> +
> +	  remote_detach_1 (ops, NULL, 0, 1);

This then ends up being just remote_detach_pid + detach_inferior, AFAICS.


> +
> +	  /* Restore the parent ptid.  */
> +	  inferior_ptid = parent_ptid;
> +	}
> +    }
> +  return 0;
>  }
>  
>  /* Same as remote_detach, but don't send the "D" packet; just disconnect.  */
> @@ -5652,6 +5719,11 @@ Packet: '%s'\n"),
>  	      p = unpack_varlen_hex (++p1, &c);
>  	      event->core = c;
>  	    }
> +	  else if (strncmp (p, "xfork", p1 - p) == 0)
> +	    {
> +	      event->ws.value.related_pid = read_ptid (++p1, &p);
> +	      event->ws.kind = TARGET_WAITKIND_FORKED;
> +	    }
>  	  else
>  	    {
>  	      ULONGEST pnum;
> @@ -9514,8 +9586,11 @@ remote_pid_to_str (struct target_ops *ops, ptid_t ptid)
>        if (ptid_equal (magic_null_ptid, ptid))
>  	xsnprintf (buf, sizeof buf, "Thread <main>");
>        else if (rs->extended && remote_multi_process_p (rs))
> -	xsnprintf (buf, sizeof buf, "Thread %d.%ld",
> -		   ptid_get_pid (ptid), ptid_get_lwp (ptid));
> +	if (ptid_get_lwp (ptid) == 0)
> +	  return normal_pid_to_str (ptid);
> +	else
> +	  xsnprintf (buf, sizeof buf, "Thread %d.%ld",
> +		     ptid_get_pid (ptid), ptid_get_lwp (ptid));
>        else
>  	xsnprintf (buf, sizeof buf, "Thread %ld",
>  		   ptid_get_lwp (ptid));
> @@ -11938,6 +12013,7 @@ Specify the serial device it is connected to (e.g. /dev/ttya).";
>    extended_remote_ops.to_kill = extended_remote_kill;
>    extended_remote_ops.to_supports_disable_randomization
>      = extended_remote_supports_disable_randomization;
> +  extended_remote_ops.to_follow_fork = remote_follow_fork;
>  }
>  
>  static int
> diff --git a/gdb/testsuite/gdb.base/multi-forks.exp b/gdb/testsuite/gdb.base/multi-forks.exp
> index e95cb4b..2a71e63 100644
> --- a/gdb/testsuite/gdb.base/multi-forks.exp
> +++ b/gdb/testsuite/gdb.base/multi-forks.exp
> @@ -62,6 +62,23 @@ proc continue_to_exit_bp_loc {} {
>      set seen_break 0
>      set seen_prompt 0
>      set seen_timeout 0
> +
> +    # If we are running with a native gdbserver, the output ($decimal done)
> +    # will come via the spawn_id of gdbserver, not the spawn_id of gdb (the
> +    # default).  So we grab the spawn_id of gdbserver, if it exists, and
> +    # add it to the gdb_expect statement below using "-i", allowing us to
> +    # apply the expect statement to the output of both spawn_ids.
> +    #
> +    # If we are running with a truly remote gdbserver (gdb,noinferiorio),
> +    # the output of the program will be inaccessible.  In this case we do
> +    # not check for the ($decimal done) output, but just look for the gdb
> +    # prompt.
> +    global server_spawn_id
> +    set current_spawn_id [board_info host fileid]
> +    if {![info exists server_spawn_id]} {
> +	set server_spawn_id ""
> +    }
> +
>      while { ($seen_done < 16 || ! $seen_prompt) && ! $seen_timeout } {
>  	# We don't know what order the interesting things will arrive in.
>  	# Using a pattern of the form 'x|y|z' instead of -re x ... -re y
> @@ -70,7 +87,8 @@ proc continue_to_exit_bp_loc {} {
>  	# first in the script that occurs anywhere in the input, so that
>  	# we don't skip anything.
>  	gdb_expect {
> -	    -re "($decimal done)|(Breakpoint)|($gdb_prompt)" {
> +	    -i "$current_spawn_id $server_spawn_id" \


Note this is currently working by chance.  We need this one in:

  https://sourceware.org/ml/gdb-patches/2015-02/msg00659.html

in order to safely be able to do '-i "$server_spawn_id"'.


> +	         -re "($decimal done)|(Breakpoint)|($gdb_prompt)" {
>  		if {[info exists expect_out(1,string)]} {
>  		    incr seen_done
>  		} elseif {[info exists expect_out(2,string)]} {

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v6 5/6] Remote follow vfork
  2015-03-17 20:58       ` [PATCH v6 5/6] Remote follow vfork Don Breazeal
@ 2015-03-24 12:28         ` Pedro Alves
  0 siblings, 0 replies; 30+ messages in thread
From: Pedro Alves @ 2015-03-24 12:28 UTC (permalink / raw)
  To: Don Breazeal, gdb-patches

On 03/17/2015 08:56 PM, Don Breazeal wrote:

> diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c
> index dc43e38..42c3ec5 100644
> --- a/gdb/gdbserver/remote-utils.c
> +++ b/gdb/gdbserver/remote-utils.c
> @@ -1115,15 +1115,19 @@ prepare_resume_reply (char *buf, ptid_t ptid,
>      {
>      case TARGET_WAITKIND_STOPPED:
>      case TARGET_WAITKIND_FORKED:
> +    case TARGET_WAITKIND_VFORKED:
>        {
>  	struct thread_info *saved_thread;
>  	const char **regp;
>  	struct regcache *regcache;
>  
> -	if (status->kind == TARGET_WAITKIND_FORKED && report_fork_events)
> +	if ((status->kind == TARGET_WAITKIND_FORKED && report_fork_events)
> +	     || (status->kind == TARGET_WAITKIND_VFORKED
> +		 && report_vfork_events))
>  	  {
>  	    enum gdb_signal signal = GDB_SIGNAL_TRAP;
> -	    const char *event = "xfork";
> +	    const char *event = (status->kind == TARGET_WAITKIND_FORKED
> +				 ? "xfork" : "vfork");
>  
>  	    sprintf (buf, "T%02x%s:", signal, event);
>  	    buf += strlen (buf);
> @@ -1245,6 +1249,15 @@ prepare_resume_reply (char *buf, ptid_t ptid,
>        else
>  	sprintf (buf, "X%02x", status->value.sig);
>        break;
> +    case TARGET_WAITKIND_VFORK_DONE:
> +      if (multi_process)
> +	{
> +	  enum gdb_signal signal = GDB_SIGNAL_TRAP;
> +	  const char *event = "vforkdone";
> +

Should only include vforkdone if report_vfork_events is true, I'd think.

> +	  sprintf (buf, "T%02x%s:;", signal, event);
> +	}
> +      break;
>      default:
>        error ("unhandled waitkind");
>        break;
> diff --git a/gdb/gdbserver/server.h b/gdb/gdbserver/server.h
> index 696a24e..09a5624 100644
> --- a/gdb/gdbserver/server.h
> +++ b/gdb/gdbserver/server.h
> @@ -85,6 +85,7 @@ extern int disable_packet_qfThreadInfo;
>  extern int run_once;
>  extern int multi_process;
>  extern int report_fork_events;
> +extern int report_vfork_events;
>  extern int non_stop;
>  
>  /* True if the "swbreak+" feature is active.  In that case, GDB wants
> diff --git a/gdb/remote.c b/gdb/remote.c
> index 899c7a2..d1ba62d 100644
> --- a/gdb/remote.c
> +++ b/gdb/remote.c
> @@ -4526,15 +4526,20 @@ remote_follow_fork (struct target_ops *ops, int follow_child,
>  {
>    struct remote_state *rs = get_remote_state ();
>  
> +  /* If fork events aren't supported, then return.  We know that if
> +     fork events are supported, then so are vfork events, since they
> +     were both introduced in the same version of the linux kernel.  */

"Linux", caps.

But this really doesn't make sense -- if we define different
qSupported features for fork and vfork, then we should check
them separately.  We don't know (and shouldn't care) whether
we're debugging against a Linux target here.  The packets/features
should work with other OSs.

>    if (remote_fork_event_p (rs))
>      {
>        if (detach_fork && !follow_child)
>  	{
>  	  ptid_t parent_ptid;
>  	  ptid_t child_ptid;
> +	  enum target_waitkind kind;
>  

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v6 6/6] Remote fork catch
  2015-03-17 20:58       ` [PATCH v6 6/6] Remote fork catch Don Breazeal
@ 2015-03-24 12:47         ` Pedro Alves
  0 siblings, 0 replies; 30+ messages in thread
From: Pedro Alves @ 2015-03-24 12:47 UTC (permalink / raw)
  To: Don Breazeal, gdb-patches

On 03/17/2015 08:56 PM, Don Breazeal wrote:

> diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
> index 8fa6f8a..346f2c4 100644
> --- a/gdb/gdbserver/server.c
> +++ b/gdb/gdbserver/server.c
> @@ -1356,6 +1356,15 @@ handle_qxfer_threads_worker (struct inferior_list_entry *inf, void *arg)
>    int core = target_core_of_thread (ptid);
>    char core_s[21];
>  
> +  /* Skip new threads created as the result of a fork if we are not done
> +     handling that fork event.  We won't know whether to tell GDB about
> +     the new thread until we are done following the fork.  */
> +  if ((last_status.kind == TARGET_WAITKIND_FORKED
> +       || last_status.kind == TARGET_WAITKIND_VFORKED)
> +      && (ptid_get_pid (last_status.value.related_pid)
> +	  == ptid_get_pid (ptid)))
> +    return;

This use of last_status here is really just as bad as
get_last_target_status, for the same reasons.  What if a thread
forks at the same time another thread hits a breakpoint, and
we end up reporting the breakpoint first, leaving the fork
pending?  Sounds like we'll end up listing the child fork
thread then.

> +
>    write_ptid (ptid_s, ptid);
>  
>    if (core != -1)
> @@ -4144,3 +4153,12 @@ handle_target_event (int err, gdb_client_data client_data)
>  
>    return 0;
>  }
> +
> +/* Retrieve the last waitstatus reported to GDB.  */
> +
> +void
> +get_last_target_status (ptid_t *ptid, struct target_waitstatus *last)
> +{
> +  *ptid = last_ptid;
> +  *last = last_status;
> +}

Looks like you forgot to delete the function.  :-)

> diff --git a/gdb/gdbserver/server.h b/gdb/gdbserver/server.h
> index 09a5624..8c6ec27 100644
> --- a/gdb/gdbserver/server.h
> +++ b/gdb/gdbserver/server.h
> @@ -113,6 +113,8 @@ typedef int gdb_fildes_t;
>  /* Functions from server.c.  */
>  extern int handle_serial_event (int err, gdb_client_data client_data);
>  extern int handle_target_event (int err, gdb_client_data client_data);
> +extern void get_last_target_status (ptid_t *ptid,
> +				    struct target_waitstatus *last);
>  
>  #include "remote-utils.h"
>  
> diff --git a/gdb/remote.c b/gdb/remote.c
> index d1ba62d..44ee89f 100644
> --- a/gdb/remote.c
> +++ b/gdb/remote.c


> @@ -8012,6 +8060,23 @@ extended_remote_kill (struct target_ops *ops)
>    int res;
>    int pid = ptid_get_pid (inferior_ptid);
>    struct remote_state *rs = get_remote_state ();
> +  struct thread_info *tp = find_thread_ptid (inferior_ptid);
> +
> +  /* If we're stopped while forking and we haven't followed yet,
> +     kill the child task.  We need to do this first because the
> +     parent will be sleeping if this is a vfork.  */
> +
> +  if (tp != NULL && (tp->pending_follow.kind == TARGET_WAITKIND_FORKED
> +		     || tp->pending_follow.kind == TARGET_WAITKIND_VFORKED))

Looks like this will miss killing the child if the user switches to
some thread other than the one that forked, in case it was a multi-threaded
program that forked.

> +    {
> +      ptid_t parent_ptid = inferior_ptid;
> +
> +      inferior_ptid = tp->pending_follow.value.related_pid;
> +      set_general_thread (inferior_ptid);
> +      extended_remote_kill (ops);
> +      inferior_ptid = parent_ptid;
> +      set_general_thread (inferior_ptid);

We never want to use the 'k' packet here, so this could
just simply be:

         int child_pid = tp->pending_follow.value.related_pid;
         remote_vkill (child_pid);

> +    }
>  
>    res = remote_vkill (pid, rs);
>    if (res == -1 && !(rs->extended && remote_multi_process_p (rs)))
> @@ -12036,6 +12101,14 @@ Specify the serial device it is connected to (e.g. /dev/ttya).";
>    extended_remote_ops.to_supports_disable_randomization
>      = extended_remote_supports_disable_randomization;
>    extended_remote_ops.to_follow_fork = remote_follow_fork;
> +  extended_remote_ops.to_insert_fork_catchpoint
> +    = remote_insert_fork_catchpoint;
> +  extended_remote_ops.to_remove_fork_catchpoint
> +    = remote_remove_fork_catchpoint;
> +  extended_remote_ops.to_insert_vfork_catchpoint
> +    = remote_insert_vfork_catchpoint;
> +  extended_remote_ops.to_remove_vfork_catchpoint
> +    = remote_remove_vfork_catchpoint;
>  }
>  
>  static int
> diff --git a/gdb/testsuite/gdb.threads/fork-thread-pending.exp b/gdb/testsuite/gdb.threads/fork-thread-pending.exp
> index d229232..594f376 100644
> --- a/gdb/testsuite/gdb.threads/fork-thread-pending.exp
> +++ b/gdb/testsuite/gdb.threads/fork-thread-pending.exp
> @@ -31,6 +31,26 @@ if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executab
>      return -1
>  }
>  
> +# Find a thread that did not fork and is not the main thread and
> +# return its thread number.  We can't just hard-code the thread
> +# number since we have no guarantee as to the ordering of the threads
> +# in gdb.  

I don't understand this -- the test runs to main first, so the main
thread should always be thread 1, no?


> We know that the main thread is in pthread_join and the
> +# forking thread is in fork, so we use this rather ungainly regexp
> +# to capture an entry from 'info threads' that doesn't show one of
> +# those routines, then extract the thread number.
> +
> +proc find_unforked_thread { } {
> +    gdb_test_multiple "info threads" "find unforked thread" {
> +        -re "(\[^\r]*Thread\[^\r]* in \[^fp]\[^ot]\[^rh]\[^kr]\[^e]\[^a]\[^d]\[^_]\[^j]\[^\r]*\r\n)" {
> +    	    regexp "(\[ 	]*)(\[0-9]*)(\[    ]*Thread\[^\r]*\r\n)" $expect_out(0,string) ignore lead_spc threadnum rest
> +        }
> +        timeout {
> +	    set threadnum -1
> +        }
> +    }
> +    return $threadnum
> +}
> +
>  clean_restart ${binfile}
>  
>  if ![runto_main] then {
> @@ -46,7 +66,8 @@ gdb_test "continue" "Catchpoint.*" "1, get to the fork event"
>  
>  gdb_test "info threads" " Thread .* Thread .* Thread .* Thread .*" "1, multiple threads found"
>  
> -gdb_test "thread 1" ".*" "1, switched away from event thread"
> +set threadnum [find_unforked_thread]
> +gdb_test "thread $threadnum" ".*" "1, switched away from event thread to thread $threadnum"
>  
>  gdb_test "continue" "Not resuming.*" "1, refused to resume"
>  
> 

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v5 01/06] Identify remote fork event support
  2015-03-24 10:57         ` Pedro Alves
@ 2015-03-26 19:00           ` Don Breazeal
  2015-03-27 11:14             ` Pedro Alves
  0 siblings, 1 reply; 30+ messages in thread
From: Don Breazeal @ 2015-03-26 19:00 UTC (permalink / raw)
  To: gdb-patches, palves

On 3/24/2015 3:57 AM, Pedro Alves wrote:
> On 03/17/2015 08:56 PM, Don Breazeal wrote:
> 
>> @@ -12508,7 +12524,8 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
>>    add_packet_config_cmd (&remote_protocol_packets[PACKET_hwbreak_feature],
>>                           "hwbreak-feature", "hwbreak-feature", 0);
>>  
>> -  /* Assert that we've registered commands for all packet configs.  */
>> +  /* Assert that we've registered "set remote foo-packet" commands
>> +     for all packet configs.  */
>>    {
>>      int i;
>>  
> 
> This hunk could go immediately/separately as obvious.

Thanks, this has been pushed.

>> @@ -12527,6 +12544,8 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
>>  	  case PACKET_DisconnectedTracing_feature:
>>  	  case PACKET_augmented_libraries_svr4_read_feature:
>>  	  case PACKET_qCRC:
>> +	  case PACKET_fork_event_feature:
>> +	  case PACKET_vfork_event_feature:
>>  	    /* Additions to this list need to be well justified:
>>  	       pre-existing packets are OK; new packets are not.  */
> 
> I think I mentioned this before: please do register commands for these
> features.  As the comment says, new packets here are not OK without
> good justification, and I can't think of a good justification.
> Then you'll need to make sure remote_query_supported only includes the
> feature in GDB's query if the feature wasn't force-disabled.  See
> the new swbreak+ feature's handling in that function.

You did mention this before, but I misunderstood.  I've made the updates
in this version.  Sorry for the repeat.

Thanks
--Don

This patch implements a mechanism for GDB to determine whether fork
events are supported in gdbserver.  This is a preparatory patch for
remote fork and exec event support.

Two new RSP packets are defined to represent fork and vfork event
support.  These packets are used similar to PACKET_swbreak_feature
to denote whether the corresponding event is supported.  GDB sends
fork-events+ and vfork-events+ to gdbserver to inquire about fork
event support.  If the response enables these packets, then GDB
knows that gdbserver supports the corresponding events and will
enable them.

Gdbserver target functions used to query for support are included,
corresponding to each new packet.

In order for gdbserver to know whether the events are supported at the
point where the qSupported packet arrives, the code in nat/linux-ptrace.c
had to be reorganized.  Previously it would test for fork/exec event
support, then enable the events using the pid of the inferior.  When the
qSupported packet arrives there may not be an inferior.  So the mechanism
was split into two parts: a function that checks whether the events are
supported, called when gdbserver starts up, and another that enables the
events when the inferior stops for the first time.

Another gdbserver change was to add some global variables similar to
multi_process, one per new packet.  These are used to control whether
the corresponding fork events are enabled.  If GDB does not inquire
about the event support in the qSupported packet, then gdbserver will
not set these "report the event" flags.  If the flags are not set, the
events are ignored like they were in the past.  Thus, gdbserver will
never send fork event notification to an older GDB that doesn't
recognize fork events.

Tested on Ubuntu x64, native/remote/extended-remote, and as part of
subsequent patches in the series.

gdb/doc/
2015-03-26  Don Breazeal  <donb@codesourcery.com>

	* gdb.texinfo (General Query Packets): Add remote protocol
	features 'fork-events' and 'vfork-events'.

gdb/gdbserver/
2015-03-26  Don Breazeal  <donb@codesourcery.com>

	* linux-low.c (linux_supports_fork_events): New function.
	(linux_supports_vfork_events): New function.
	(linux_target_ops): Initialize new structure members.
	(initialize_low): Call linux_check_ptrace_features.
	* lynx-low.c (lynx_target_ops): Initialize new structure
	members.
	* server.c (report_fork_events, report_vfork_events):
	New global flags.
	(handle_query): Add new features to qSupported packet.
	(captured_main): Initialize new global variables.
	* target.h (struct target_ops) <supports_fork_events>:
	New member.
	<supports_vfork_events>: New member.
	(target_supports_fork_events): New macro.
	(target_supports_vfork_events): New macro.
	* win32-low.c (win32_target_ops): Initialize new structure
	members.

gdb/
2015-03-26  Don Breazeal  <donb@codesourcery.com>

	* NEWS: Announce new fork event feature support in
	qSupported packet.
	* nat/linux-ptrace.c (linux_check_ptrace_features): Change
	from static to extern.
	* nat/linux-ptrace.h (linux_check_ptrace_features): Declare.
	* remote.c (anonymous enum): <PACKET_fork_event_feature,
	* PACKET_vfork_event_feature>: New enumeration constants.
	(remote_query_supported): Add new feature queries to qSupported
	packet.
	(remote_protocol_features): Add table entries for new packets.
	(_initialize_remote): Implement 'set remote foo-packet'
	commands for new packets.

---
 gdb/NEWS                  |  3 +++
 gdb/doc/gdb.texinfo       | 12 ++++++++++++
 gdb/gdbserver/linux-low.c | 21 +++++++++++++++++++++
 gdb/gdbserver/lynx-low.c  |  2 ++
 gdb/gdbserver/server.c    | 22 ++++++++++++++++++++++
 gdb/gdbserver/target.h    | 14 ++++++++++++++
 gdb/gdbserver/win32-low.c |  2 ++
 gdb/nat/linux-ptrace.c    |  2 +-
 gdb/nat/linux-ptrace.h    |  1 +
 gdb/remote.c              | 26 ++++++++++++++++++++++++++
 10 files changed, 104 insertions(+), 1 deletion(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index 3fa33c9..1d24b3b 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -96,6 +96,9 @@ vFile:fstat:
 HP/PA running HP-UX           hppa*-*-hpux*
 Itanium running HP-UX         ia64-*-hpux*
 
+* The remote stub now reports support for fork and vfork events to GDB's
+qSupported query.
+
 *** Changes in GDB 7.9
 
 * GDB now supports hardware watchpoints on x86 GNU Hurd.
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index c03ecb0..a1d312e 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -35812,6 +35812,18 @@ extensions unless the stub also reports that it supports them by
 including @samp{multiprocess+} in its @samp{qSupported} reply.
 @xref{multiprocess extensions}, for details.
 
+@item fork-events
+This feature indicates whether @value{GDBN} supports fork event 
+extensions to the remote protocol.  @value{GDBN} does not use such
+extensions unless the stub also reports that it supports them by
+including @samp{fork-events+} in its @samp{qSupported} reply.
+
+@item vfork-events
+This feature indicates whether @value{GDBN} supports vfork event 
+extensions to the remote protocol.  @value{GDBN} does not use such
+extensions unless the stub also reports that it supports them by
+including @samp{vfork-events+} in its @samp{qSupported} reply.
+
 @item xmlRegisters
 This feature indicates that @value{GDBN} supports the XML target
 description.  If the stub sees @samp{xmlRegisters=} with target
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index e4c5420..06ac673 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -5421,6 +5421,22 @@ linux_supports_multi_process (void)
   return 1;
 }
 
+/* Check if fork events are supported.  */
+
+static int
+linux_supports_fork_events (void)
+{
+  return linux_supports_tracefork ();
+}
+
+/* Check if vfork events are supported.  */
+
+static int
+linux_supports_vfork_events (void)
+{
+  return linux_supports_tracefork ();
+}
+
 static int
 linux_supports_disable_randomization (void)
 {
@@ -6393,6 +6409,8 @@ static struct target_ops linux_target_ops = {
   linux_async,
   linux_start_non_stop,
   linux_supports_multi_process,
+  linux_supports_fork_events,
+  linux_supports_vfork_events,
 #ifdef USE_THREAD_DB
   thread_db_handle_monitor_command,
 #else
@@ -6469,4 +6487,7 @@ initialize_low (void)
   sigaction (SIGCHLD, &sigchld_action, NULL);
 
   initialize_low_arch ();
+
+  /* Enable extended ptrace events.  */
+  linux_check_ptrace_features ();
 }
diff --git a/gdb/gdbserver/lynx-low.c b/gdb/gdbserver/lynx-low.c
index 2f85829..687cce0 100644
--- a/gdb/gdbserver/lynx-low.c
+++ b/gdb/gdbserver/lynx-low.c
@@ -758,6 +758,8 @@ static struct target_ops lynx_target_ops = {
   NULL,  /* async */
   NULL,  /* start_non_stop */
   NULL,  /* supports_multi_process */
+  NULL,  /* supports_fork_events */
+  NULL,  /* supports_vfork_events */
   NULL,  /* handle_monitor_command */
 };
 
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index 96b31b8..dcedea8 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -57,6 +57,8 @@ static int exit_requested;
 int run_once;
 
 int multi_process;
+int report_fork_events;
+int report_vfork_events;
 int non_stop;
 int swbreak_feature;
 int hwbreak_feature;
@@ -1992,6 +1994,18 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
 		  if (target_supports_stopped_by_hw_breakpoint ())
 		    hwbreak_feature = 1;
 		}
+	      else if (strcmp (p, "fork-events+") == 0)
+		{
+		  /* GDB supports and wants fork events if possible.  */
+		  if (target_supports_fork_events ())
+		    report_fork_events = 1;
+		}
+	      else if (strcmp (p, "vfork-events+") == 0)
+		{
+		  /* GDB supports and wants vfork events if possible.  */
+		  if (target_supports_vfork_events ())
+		    report_vfork_events = 1;
+		}
 	      else
 		target_process_qsupported (p);
 
@@ -2042,6 +2056,12 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
       if (target_supports_multi_process ())
 	strcat (own_buf, ";multiprocess+");
 
+      if (target_supports_fork_events ())
+	strcat (own_buf, ";fork-events+");
+
+      if (target_supports_vfork_events ())
+	strcat (own_buf, ";vfork-events+");
+
       if (target_supports_non_stop ())
 	strcat (own_buf, ";QNonStop+");
 
@@ -3388,6 +3408,8 @@ captured_main (int argc, char *argv[])
 
       noack_mode = 0;
       multi_process = 0;
+      report_fork_events = 0;
+      report_vfork_events = 0;
       /* Be sure we're out of tfind mode.  */
       current_traceframe = -1;
       cont_thread = null_ptid;
diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
index 126c861..dc51627 100644
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -277,6 +277,12 @@ struct target_ops
   /* Returns true if the target supports multi-process debugging.  */
   int (*supports_multi_process) (void);
 
+  /* Returns true if fork events are supported.  */
+  int (*supports_fork_events) (void);
+
+  /* Returns true if vfork events are supported.  */
+  int (*supports_vfork_events) (void);
+
   /* If not NULL, target-specific routine to process monitor command.
      Returns 1 if handled, or 0 to perform default processing.  */
   int (*handle_monitor_command) (char *);
@@ -408,6 +414,14 @@ void set_target_ops (struct target_ops *);
 
 int kill_inferior (int);
 
+#define target_supports_fork_events() \
+  (the_target->supports_fork_events ? \
+   (*the_target->supports_fork_events) () : 0)
+
+#define target_supports_vfork_events() \
+  (the_target->supports_vfork_events ? \
+   (*the_target->supports_vfork_events) () : 0)
+
 #define detach_inferior(pid) \
   (*the_target->detach) (pid)
 
diff --git a/gdb/gdbserver/win32-low.c b/gdb/gdbserver/win32-low.c
index 6c86765..5f50e46 100644
--- a/gdb/gdbserver/win32-low.c
+++ b/gdb/gdbserver/win32-low.c
@@ -1827,6 +1827,8 @@ static struct target_ops win32_target_ops = {
   NULL, /* async */
   NULL, /* start_non_stop */
   NULL, /* supports_multi_process */
+  NULL, /* supports_fork_events */
+  NULL, /* supports_vfork_events */
   NULL, /* handle_monitor_command */
   NULL, /* core_of_thread */
   NULL, /* read_loadmap */
diff --git a/gdb/nat/linux-ptrace.c b/gdb/nat/linux-ptrace.c
index 2244d9d..aba3da8 100644
--- a/gdb/nat/linux-ptrace.c
+++ b/gdb/nat/linux-ptrace.c
@@ -337,7 +337,7 @@ static void linux_test_for_exitkill (int child_pid);
 
 /* Determine ptrace features available on this target.  */
 
-static void
+void
 linux_check_ptrace_features (void)
 {
   int child_pid, ret, status;
diff --git a/gdb/nat/linux-ptrace.h b/gdb/nat/linux-ptrace.h
index 8354a4d..03d98c9 100644
--- a/gdb/nat/linux-ptrace.h
+++ b/gdb/nat/linux-ptrace.h
@@ -149,6 +149,7 @@ extern void linux_ptrace_attach_fail_reason (pid_t pid, struct buffer *buffer);
 extern char *linux_ptrace_attach_fail_reason_string (ptid_t ptid, int err);
 
 extern void linux_ptrace_init_warnings (void);
+extern void linux_check_ptrace_features (void);
 extern void linux_enable_event_reporting (pid_t pid, int attached);
 extern void linux_disable_event_reporting (pid_t pid);
 extern int linux_supports_tracefork (void);
diff --git a/gdb/remote.c b/gdb/remote.c
index 31c933b..95fc0c5 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -1343,6 +1343,12 @@ enum {
   /* Support for hwbreak+ feature.  */
   PACKET_hwbreak_feature,
 
+  /* Support for fork events.  */
+  PACKET_fork_event_feature,
+
+  /* Support for vfork events.  */
+  PACKET_vfork_event_feature,
+
   PACKET_MAX
 };
 
@@ -4042,6 +4048,10 @@ static const struct protocol_feature remote_protocol_features[] = {
     PACKET_Qbtrace_conf_bts_size },
   { "swbreak", PACKET_DISABLE, remote_supported_packet, PACKET_swbreak_feature },
   { "hwbreak", PACKET_DISABLE, remote_supported_packet, PACKET_hwbreak_feature },
+  { "fork-events", PACKET_DISABLE, remote_supported_packet,
+    PACKET_fork_event_feature },
+  { "vfork-events", PACKET_DISABLE, remote_supported_packet,
+    PACKET_vfork_event_feature },
 };
 
 static char *remote_support_xml;
@@ -4120,6 +4130,16 @@ remote_query_supported (void)
 
       q = remote_query_supported_append (q, "qRelocInsn+");
 
+      if (rs->extended)
+	{
+	  if (packet_set_cmd_state (PACKET_fork_event_feature)
+	      != AUTO_BOOLEAN_FALSE)
+	    q = remote_query_supported_append (q, "fork-events+");
+	  if (packet_set_cmd_state (PACKET_vfork_event_feature)
+	      != AUTO_BOOLEAN_FALSE)
+	    q = remote_query_supported_append (q, "vfork-events+");
+	}
+
       q = reconcat (q, "qSupported:", q, (char *) NULL);
       putpkt (q);
 
@@ -12495,6 +12515,12 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
   add_packet_config_cmd (&remote_protocol_packets[PACKET_hwbreak_feature],
                          "hwbreak-feature", "hwbreak-feature", 0);
 
+  add_packet_config_cmd (&remote_protocol_packets[PACKET_fork_event_feature],
+			 "fork-event-feature", "fork-event-feature", 0);
+
+  add_packet_config_cmd (&remote_protocol_packets[PACKET_vfork_event_feature],
+			 "vfork-event-feature", "vfork-event-feature", 0);
+
   /* Assert that we've registered "set remote foo-packet" commands
      for all packet configs.  */
   {
-- 
1.8.1.1

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v5 01/06] Identify remote fork event support
  2015-03-26 19:00           ` [PATCH v5 01/06] " Don Breazeal
@ 2015-03-27 11:14             ` Pedro Alves
  0 siblings, 0 replies; 30+ messages in thread
From: Pedro Alves @ 2015-03-27 11:14 UTC (permalink / raw)
  To: Don Breazeal, gdb-patches

On 03/26/2015 07:00 PM, Don Breazeal wrote:
> On 3/24/2015 3:57 AM, Pedro Alves wrote:
>> On 03/17/2015 08:56 PM, Don Breazeal wrote:
>>
>>> @@ -12508,7 +12524,8 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
>>>    add_packet_config_cmd (&remote_protocol_packets[PACKET_hwbreak_feature],
>>>                           "hwbreak-feature", "hwbreak-feature", 0);
>>>  
>>> -  /* Assert that we've registered commands for all packet configs.  */
>>> +  /* Assert that we've registered "set remote foo-packet" commands
>>> +     for all packet configs.  */
>>>    {
>>>      int i;
>>>  
>>
>> This hunk could go immediately/separately as obvious.
> 
> Thanks, this has been pushed.
> 
>>> @@ -12527,6 +12544,8 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
>>>  	  case PACKET_DisconnectedTracing_feature:
>>>  	  case PACKET_augmented_libraries_svr4_read_feature:
>>>  	  case PACKET_qCRC:
>>> +	  case PACKET_fork_event_feature:
>>> +	  case PACKET_vfork_event_feature:
>>>  	    /* Additions to this list need to be well justified:
>>>  	       pre-existing packets are OK; new packets are not.  */
>>
>> I think I mentioned this before: please do register commands for these
>> features.  As the comment says, new packets here are not OK without
>> good justification, and I can't think of a good justification.
>> Then you'll need to make sure remote_query_supported only includes the
>> feature in GDB's query if the feature wasn't force-disabled.  See
>> the new swbreak+ feature's handling in that function.
> 
> You did mention this before, but I misunderstood.  I've made the updates
> in this version.  Sorry for the repeat.

Sorry, I should probably have been clearer before then.

The "pre-existing packets are OK" comment just means that
when the assertion was added, those packets already existed.
It'd be great to just add the commands for those, and get
rid of that exceptions list...

The code looks good to me.  The docs still need a few tweaks:

> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -35812,6 +35812,18 @@ extensions unless the stub also reports that it supports them by
>  including @samp{multiprocess+} in its @samp{qSupported} reply.
>  @xref{multiprocess extensions}, for details.
>  
> +@item fork-events
> +This feature indicates whether @value{GDBN} supports fork event 
> +extensions to the remote protocol.  @value{GDBN} does not use such
> +extensions unless the stub also reports that it supports them by
> +including @samp{fork-events+} in its @samp{qSupported} reply.
> +
> +@item vfork-events
> +This feature indicates whether @value{GDBN} supports vfork event 
> +extensions to the remote protocol.  @value{GDBN} does not use such
> +extensions unless the stub also reports that it supports them by
> +including @samp{vfork-events+} in its @samp{qSupported} reply.
> +
>  @item xmlRegisters
>  This feature indicates that @value{GDBN} supports the XML target
>  description.  If the stub sees @samp{xmlRegisters=} with target
> diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
> index e4c5420..06ac673 100644

You'll need to update the tables below, where it reads:

"These are the currently defined stub features and their properties"

and:

"These are the currently defined stub features, in more detail"

and also, mention the new commands in:

"For each packet @var{name}, the command to enable or disable the
packet is @code{set remote @var{name}-packet}.  The available settings
are:"

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v6 3/6] Extended-remote Linux follow fork
  2015-03-24 12:17         ` Pedro Alves
@ 2015-04-07 17:35           ` Pedro Alves
  0 siblings, 0 replies; 30+ messages in thread
From: Pedro Alves @ 2015-04-07 17:35 UTC (permalink / raw)
  To: Don Breazeal, gdb-patches

On 03/24/2015 12:16 PM, Pedro Alves wrote:
>> > diff --git a/gdb/testsuite/gdb.base/multi-forks.exp b/gdb/testsuite/gdb.base/multi-forks.exp
>> > index e95cb4b..2a71e63 100644
>> > --- a/gdb/testsuite/gdb.base/multi-forks.exp
>> > +++ b/gdb/testsuite/gdb.base/multi-forks.exp
>> > @@ -62,6 +62,23 @@ proc continue_to_exit_bp_loc {} {
>> >      set seen_break 0
>> >      set seen_prompt 0
>> >      set seen_timeout 0
>> > +
>> > +    # If we are running with a native gdbserver, the output ($decimal done)
>> > +    # will come via the spawn_id of gdbserver, not the spawn_id of gdb (the
>> > +    # default).  So we grab the spawn_id of gdbserver, if it exists, and
>> > +    # add it to the gdb_expect statement below using "-i", allowing us to
>> > +    # apply the expect statement to the output of both spawn_ids.
>> > +    #
>> > +    # If we are running with a truly remote gdbserver (gdb,noinferiorio),
>> > +    # the output of the program will be inaccessible.  In this case we do
>> > +    # not check for the ($decimal done) output, but just look for the gdb
>> > +    # prompt.
>> > +    global server_spawn_id
>> > +    set current_spawn_id [board_info host fileid]
>> > +    if {![info exists server_spawn_id]} {
>> > +	set server_spawn_id ""
>> > +    }
>> > +
>> >      while { ($seen_done < 16 || ! $seen_prompt) && ! $seen_timeout } {
>> >  	# We don't know what order the interesting things will arrive in.
>> >  	# Using a pattern of the form 'x|y|z' instead of -re x ... -re y
>> > @@ -70,7 +87,8 @@ proc continue_to_exit_bp_loc {} {
>> >  	# first in the script that occurs anywhere in the input, so that
>> >  	# we don't skip anything.
>> >  	gdb_expect {
>> > -	    -re "($decimal done)|(Breakpoint)|($gdb_prompt)" {
>> > +	    -i "$current_spawn_id $server_spawn_id" \
> 
> Note this is currently working by chance.  We need this one in:
> 
>   https://sourceware.org/ml/gdb-patches/2015-02/msg00659.html
> 
> in order to safely be able to do '-i "$server_spawn_id"'.

FYI, I now pushed that series in.  Please update this to
use $inferior_spawn_id instead.

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 30+ messages in thread

end of thread, other threads:[~2015-04-07 17:35 UTC | newest]

Thread overview: 30+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <201501252151.t0PLo3jE018308@mx0b-0013c101.pphosted.com>
2015-01-26  4:43 ` [PATCH v4 0/7] Remote fork events Breazeal, Don
2015-02-06 20:02   ` Breazeal, Don
2015-02-27  0:46   ` [PATCH v5 0/6] " Don Breazeal
2015-02-27  0:46     ` [PATCH v5 01/06] Identify remote fork event support Don Breazeal
2015-02-27  0:47     ` [PATCH v5 04/06] Arch-specific remote follow fork Don Breazeal
2015-02-27  7:37       ` Eli Zaretskii
2015-02-27  0:47     ` [PATCH v5 02/06] Clone remote breakpoints Don Breazeal
2015-02-27  0:47     ` [PATCH v5 03/06] Extended-remote Linux follow fork Don Breazeal
2015-02-27  0:47     ` [PATCH v5 05/06] Remote follow vfork Don Breazeal
2015-02-27  0:48     ` [PATCH v5 06/06] Remote fork catch Don Breazeal
2015-03-02 18:29     ` [PATCH v5 0/6] Remote fork events Pedro Alves
2015-03-02 20:18     ` [PATCH] Tighten gdb.base/disp-step-syscall.exp (was: Re: [PATCH v5 0/6] Remote fork events) Pedro Alves
2015-03-03  6:20       ` [PATCH] Tighten gdb.base/disp-step-syscall.exp Breazeal, Don
2015-03-03 15:01         ` Pedro Alves
2015-03-17 21:18       ` Breazeal, Don
2015-03-18 19:37         ` Pedro Alves
2015-03-17 20:56     ` [PATCH v6 0/6] Remote fork events Don Breazeal
2015-03-17 20:56       ` [PATCH v6 1/6] Identify remote fork event support Don Breazeal
2015-03-24 10:57         ` Pedro Alves
2015-03-26 19:00           ` [PATCH v5 01/06] " Don Breazeal
2015-03-27 11:14             ` Pedro Alves
2015-03-17 20:57       ` [PATCH v6 3/6] Extended-remote Linux follow fork Don Breazeal
2015-03-24 12:17         ` Pedro Alves
2015-04-07 17:35           ` Pedro Alves
2015-03-17 20:57       ` [PATCH v6 4/6] Arch-specific remote " Don Breazeal
2015-03-17 20:57       ` [PATCH v6 2/6] Clone remote breakpoints Don Breazeal
2015-03-17 20:58       ` [PATCH v6 5/6] Remote follow vfork Don Breazeal
2015-03-24 12:28         ` Pedro Alves
2015-03-17 20:58       ` [PATCH v6 6/6] Remote fork catch Don Breazeal
2015-03-24 12:47         ` Pedro Alves

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).