This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[RFC] [PATCH] Add gdbserver multiclient support for strace


Looking for comments, thoughts, and suggestions on this patch
to add multiclient support for strace.  The method essentially has
gdbserver timeshare each client: gdb client is active, a syscall
occurs that needs strace's attention so that client becomes active,
that client continues then the gdb client then again becomes active.

Details of what changed and the methodolgy is at:
 https://sourceware.org/gdb/wiki/MultiClient

There is also a pending patch for strace that adds a gdbserver
backend.  That patch adds a backend dispatch table with the ptrace
backend being the default.  The goal is to make use of this
functionality coexisting with gdbserver, which is debugging the same
process.  Additionally strace can be used standalone using a current
gdbserver:

% strace -G localhost:12345 syscall-test.x
brk(NULL)                               = 0x602000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or
directory)
openat(AT_FDCWD, "/home/scox/lib/tls/haswell/x86_64/libpthread.so.0",
O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
...

It is a large patch so will certainly require a few iterations. One thought is to split out the pieces that add another client: event-loop.c and server.h.


gdb/testsuite/Changelog
    * multi-client.exp:  New test.
    * strace-threads.c:  New test program.


gdb/gdbserver/ChangeLog
    * event-loop.c (delete_file_handler):  Keep active client if there
    are multiple clients.
    (wait_for_event): Look for and add additional clients.

    * linux-low.c (gdb_catch_this_syscall_p):  Allow for temporarily
turning off syscall catching in situations which may confuse multiple clients.

    * notif.c (notif_write_event):  Call notify_clients for multiple
    client notification.
    (notif_push): Likewise.

* remote-utils.c (get_remote_desc, set_remote_desc, get_listen_desc): New
    (handle_accept_event):  Make extern
    CHECK
    (write_prim, read_prim, putpkt_binary_1, readchar, getpkt)
(look_up_one_symbol, relocate_instruction): Add fd, include fd in debug output

* remote-utils.h (get_remote_desc, set_remote_desc, get_listen_desc): New

    * server.h (notify_clients, NO_SYSCALL, displaced_step_type)
    (packet_types): New
    (file_desc, packet_type, last_packet_type, displaced_step)
    (pending, nonstop_pending, catch_syscalls, syscall_op)
    (last_cont_ptid, new_general_thread, last_cont_status):  New
    members of client_state
    (struct multi_client_states):  New list for multiple client.

    * server.c (attach_count, get_client_states, pending_types)
    (nonstop_pending_types, multi_client_states::set_client_state)
    (client_state::client_state, client_state::copy_status)
    (attached_to_same_proc, dump_client_state, add_client_by_pid)
    (get_first_client, get_first_client_fd)
    (have_multiple_clients, multi_client_states::delete_client_state)
    (get_packet_type):  client_state and multi_client_states
    management routines
(resolve_waiter): New to resolve a client that is waiting for a reply to
    a vCont packet.
    (analyze_group):  New to determine if another client waiting on us?
(setup_multiplexing): New to determine if one of our clients should wait
    for another client

    (notify_clients):  New to notify a non-stop client
    (do_multiplexing): New to determine if a waiting client can be
    resolved.
    (handle_general_set):  Don't allow non-stop state change
    (handle_monitor_command): Add client status
    (handle_detach, handle_v_attach):  Allow for multiple clients
    (captured_main)  Setup the initial client state
    (process_serial_event):  Call setup_multiplexing and
    do_multiplexing
    (dump_stop_queue):  New
    (handle_target_event): Set client nonstop status

 gas/testsuite/gas/arm/pr24907.d           |   19 +
 gas/testsuite/gas/arm/pr24907.s           |   16 +
 gdb/contrib/gdbrp-dump.py                 |  744 +++++++++++++++++++
 gdb/gdbserver/ChangeLog                   |  213 ++++++
 gdb/gdbserver/event-loop.c                |   44 +-
 gdb/gdbserver/linux-low.c                 |    3 +
 gdb/gdbserver/notif.c                     |    4 +-
 gdb/gdbserver/remote-utils.c              |  109 ++-
 gdb/gdbserver/remote-utils.h              |    9 +-
gdb/gdbserver/server.c | 1103 ++++++++++++++++++++++++++++-
 gdb/gdbserver/server.h                    |   58 +-
 gdb/remote.c                              |    2 +-
 gdb/testsuite/ChangeLog                   |   44 ++
 gdb/testsuite/gdb.server/multi-client.exp |  440 ++++++++++++
 gdb/testsuite/gdb.server/strace-threads.c |   76 ++
 gdb/testsuite/gdb.server/towers.c         |  153 ++++
 16 files changed, 2983 insertions(+), 54 deletions(-)

diff --git a/gdb/gdbserver/event-loop.c b/gdb/gdbserver/event-loop.c
index bcc496eee0f..0209fdde26a 100644
--- a/gdb/gdbserver/event-loop.c
+++ b/gdb/gdbserver/event-loop.c
@@ -21,2 +21,3 @@
 #include "server.h"
+#include "remote-utils.h"

@@ -377,3 +378,7 @@ delete_file_handler (gdb_fildes_t fd)
     }
-  free (file_ptr);
+
+  /* Do not free in case another client tries to attach */
+  /* free (file_ptr); */
+  struct multi_client_states & client_states = get_client_states();
+  client_states.delete_client_state (fd);
 }
@@ -417,2 +422,8 @@ handle_file_event (gdb_fildes_t event_file_desc)
 	    {
+	      /* Switch client states if we have multiple clients */
+	      if (have_multiple_clients (file_ptr->fd))
+		{
+		  struct multi_client_states & client_states = get_client_states();
+		  client_states.set_client_state (file_ptr->fd);
+		}
 	      if ((*file_ptr->proc) (file_ptr->error,
@@ -457,2 +468,33 @@ wait_for_event (void)

+  /* Do we have another client? */
+  fd_set conn_fd_set;
+  struct timeval timeout;
+  FD_ZERO(&conn_fd_set);
+  FD_SET(get_listen_desc(),&conn_fd_set);
+  timeout.tv_sec = 0;
+  timeout.tv_usec = 10;
+  num_found = select (FD_SETSIZE, &conn_fd_set, NULL, NULL, &timeout);
+  if (debug_threads > 1)
+ fprintf (stderr,"%s select fd=%d found=%d\n",__FUNCTION__,get_listen_desc(),num_found);
+  if (num_found > 0)
+    {
+    int i;
+      for (i = 1; i < FD_SETSIZE; ++i)
+	if (FD_ISSET (i, &conn_fd_set))
+	  {
+	    if (i == get_listen_desc())
+	      {
+		client_state &cs = get_client_state();
+		if (debug_threads > 1)
+		  fprintf (stderr,"%s in select idx %d\n",__FUNCTION__,i);
+		/* instead of using gdb_event just setup the connection "by hand" */
+		handle_accept_event (0, NULL);
+		add_file_handler (get_remote_desc(), handle_serial_event, &cs);
+	      }
+	    else
+	      if (debug_threads > 1)
+ fprintf (stderr,"%s data arrived on existing connection %d fd=%d\n", __FUNCTION__, i,get_listen_desc());
+	  }
+    }
+
   /* Make sure all output is done before getting another event.  */

diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 3113017ae67..857653e10bb 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -3062,2 +3062,5 @@ gdb_catch_this_syscall_p (struct lwp_info *event_child)

+  if (proc->syscalls_to_catch[0] == NO_SYSCALL)
+    return 0;
+
   if (proc->syscalls_to_catch[0] == ANY_SYSCALL)

diff --git a/gdb/gdbserver/notif.c b/gdb/gdbserver/notif.c
index 0b526eda503..351e5f4db9c 100644
--- a/gdb/gdbserver/notif.c
+++ b/gdb/gdbserver/notif.c
@@ -68,2 +68,3 @@ notif_write_event (struct notif_server *notif, char *own_buf)
       notif->write (event, own_buf);
+      notify_clients (own_buf, 0);
     }
@@ -151,3 +152,4 @@ notif_push (struct notif_server *np, struct notif_event *new_event)
       np->write (new_event, p);
-      putpkt_notif (buf);
+      if (notify_clients (buf, 1))
+	putpkt_notif (buf);
     }

diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c
index 665fc66c53d..17f53d3e2c8 100644
--- a/gdb/gdbserver/remote-utils.c
+++ b/gdb/gdbserver/remote-utils.c
@@ -94,3 +94,3 @@ static int readchar_callback = NOT_SCHEDULED;

-static int readchar (void);
+static int readchar (gdb_fildes_t);
 static void reset_readchar (void);
@@ -120,2 +120,21 @@ extern int debug_threads;

+
+int
+get_remote_desc (void)
+{
+  return remote_desc;
+}
+
+void
+set_remote_desc (gdb_fildes_t fd)
+{
+  remote_desc = fd;
+}
+
+int
+get_listen_desc (void)
+{
+  return listen_desc;
+}
+
 int
@@ -148,3 +167,3 @@ enable_async_notification (int fd)

-static int
+int
 handle_accept_event (int err, gdb_client_data client_data)
@@ -161,2 +180,6 @@ handle_accept_event (int err, gdb_client_data client_data)

+  struct multi_client_states & client_states = get_client_states();
+  client_state *cs = client_states.set_client_state (remote_desc);
+  cs->noack_mode = 0;
+
   /* Enable TCP keep alive process. */
@@ -177,3 +200,4 @@ handle_accept_event (int err, gdb_client_data client_data)

-  if (run_once)
+  /* Do not close in case another client tries to attach */
+  if (0 && run_once)
     {
@@ -188,3 +212,4 @@ handle_accept_event (int err, gdb_client_data client_data) descriptor open for add_file_handler to wait for a new connection. */
-  delete_file_handler (listen_desc);
+  /* Do not delete in case another client tries to attach */
+  /*  delete_file_handler (listen_desc); */

@@ -216,3 +241,5 @@ handle_accept_event (int err, gdb_client_data client_data)
      threads' status ('?').  */
-  target_async (0);
+
+  /* Do not async in case another client tries to attach */
+  /* target_async (0); */

@@ -612,3 +639,3 @@ read_ptid (const char *buf, const char **obuf)
 static int
-write_prim (const void *buf, int count)
+write_prim (gdb_fildes_t fd, const void *buf, int count)
 {
@@ -617,3 +644,3 @@ write_prim (const void *buf, int count)
   else
-    return write (remote_desc, buf, count);
+    return write (fd, buf, count);
 }
@@ -625,3 +652,3 @@ write_prim (const void *buf, int count)
 static int
-read_prim (void *buf, int count)
+read_prim (gdb_fildes_t fd, void *buf, int count)
 {
@@ -630,3 +657,3 @@ read_prim (void *buf, int count)
   else
-    return read (remote_desc, buf, count);
+    return read (fd, buf, count);
 }
@@ -671,3 +698,3 @@ putpkt_binary_1 (char *buf, int cnt, int is_notif)
     {
-      if (write_prim (buf2, p - buf2) != p - buf2)
+      if (write_prim (cs.file_desc, buf2, p - buf2) != p - buf2)
 	{
@@ -684,6 +711,6 @@ putpkt_binary_1 (char *buf, int cnt, int is_notif)
 	      if (is_notif)
-		debug_printf ("putpkt (\"%s\"); [notif]\n", buf2);
+	  	debug_printf ("putpkt/%d (\"%s\"); [notif]\n", cs.file_desc, buf2);
 	      else
-		debug_printf ("putpkt (\"%s\"); [noack mode]\n", buf2);
-	      debug_flush ();
+		debug_printf ("putpkt/%d (\"%s\"); [noack mode]\n", cs.file_desc, buf2);
+	      fflush (stderr);
 	    }
@@ -694,7 +721,7 @@ putpkt_binary_1 (char *buf, int cnt, int is_notif)
 	{
-	  debug_printf ("putpkt (\"%s\"); [looking for ack]\n", buf2);
-	  debug_flush ();
+ debug_printf ("putpkt/%d (\"%s\"); [looking for ack]\n", cs.file_desc, buf2);
+	  fflush (stderr);
 	}

-      cc = readchar ();
+      cc = readchar (cs.file_desc);

@@ -766,3 +793,3 @@ input_interrupt (int unused)

-      cc = read_prim (&c, 1);
+      cc = read_prim (remote_desc, &c, 1);

@@ -896,3 +923,3 @@ static unsigned char *readchar_bufp;
 static int
-readchar (void)
+readchar (gdb_fildes_t fd)
 {
@@ -902,3 +929,3 @@ readchar (void)
     {
-      readchar_bufcnt = read_prim (readchar_buf, sizeof (readchar_buf));
+ readchar_bufcnt = read_prim (fd, readchar_buf, sizeof (readchar_buf));

@@ -971,3 +998,3 @@ reschedule (void)
 int
-getpkt (char *buf)
+getpkt (gdb_fildes_t fd, char *buf)
 {
@@ -984,3 +1011,3 @@ getpkt (char *buf)
 	{
-	  c = readchar ();
+	  c = readchar (fd);

@@ -1009,3 +1036,3 @@ getpkt (char *buf)
 	{
-	  c = readchar ();
+	  c = readchar (fd);
 	  if (c < 0)
@@ -1019,4 +1046,4 @@ getpkt (char *buf)

-      c1 = fromhex (readchar ());
-      c2 = fromhex (readchar ());
+      c1 = fromhex (readchar (fd));
+      c2 = fromhex (readchar (fd));

@@ -1037,3 +1064,3 @@ getpkt (char *buf)
 	       (c1 << 4) + c2, csum, buf);
-      if (write_prim ("-", 1) != 1)
+      if (write_prim (fd, "-", 1) != 1)
 	return -1;
@@ -1045,7 +1072,7 @@ getpkt (char *buf)
 	{
-	  debug_printf ("getpkt (\"%s\");  [sending ack] \n", buf);
-	  debug_flush ();
+	  debug_printf ("getpkt/%d (\"%s\");  [sending ack] \n", fd, buf);
+	  fflush (stderr);
 	}

-      if (write_prim ("+", 1) != 1)
+      if (write_prim (fd, "+", 1) != 1)
 	return -1;
@@ -1062,4 +1089,4 @@ getpkt (char *buf)
 	{
-	  debug_printf ("getpkt (\"%s\");  [no ack sent] \n", buf);
-	  debug_flush ();
+	  debug_printf ("getpkt/%d (\"%s\");  [no ack sent] \n", fd, buf);
+	  fflush (stderr);
 	}
@@ -1079,3 +1106,3 @@ getpkt (char *buf)
       /* Consume the interrupt character in the buffer.  */
-      readchar ();
+      readchar (fd);
       (*the_target->request_interrupt) ();
@@ -1282,2 +1309,14 @@ prepare_resume_reply (char *buf, ptid_t ptid,
 		buf += strlen (buf);
+#if 1
+		if (non_stop
+		    && (status->kind == TARGET_WAITKIND_SYSCALL_ENTRY
+			|| status->kind == TARGET_WAITKIND_SYSCALL_RETURN))
+		  {
+		    sprintf (buf, "thread:");
+		    buf += strlen (buf);
+		    buf = write_ptid (buf, cs.general_thread);
+		    strcat (buf, ";");
+		    buf += strlen (buf);
+		  }
+#endif

@@ -1515,3 +1554,3 @@ look_up_one_symbol (const char *name, CORE_ADDR *addrp, int may_ask_gdb)
   /* FIXME:  Eventually add buffer overflow checking (to getpkt?)  */
-  len = getpkt (cs.own_buf);
+  len = getpkt (cs.file_desc, cs.own_buf);
   if (len < 0)
@@ -1553,3 +1592,3 @@ look_up_one_symbol (const char *name, CORE_ADDR *addrp, int may_ask_gdb)
 	break;
-      len = getpkt (cs.own_buf);
+      len = getpkt (cs.file_desc, cs.own_buf);
       if (len < 0)
@@ -1611,3 +1650,3 @@ relocate_instruction (CORE_ADDR *to, CORE_ADDR oldloc)
   /* FIXME:  Eventually add buffer overflow checking (to getpkt?)  */
-  len = getpkt (cs.own_buf);
+  len = getpkt (cs.file_desc, cs.own_buf);
   if (len < 0)
@@ -1654,3 +1693,3 @@ relocate_instruction (CORE_ADDR *to, CORE_ADDR oldloc)
 	return -1;
-      len = getpkt (cs.own_buf);
+      len = getpkt (cs.file_desc, cs.own_buf);
       if (len < 0)

diff --git a/gdb/gdbserver/remote-utils.h b/gdb/gdbserver/remote-utils.h
index 4ca5d9435e5..f12152f21fe 100644
--- a/gdb/gdbserver/remote-utils.h
+++ b/gdb/gdbserver/remote-utils.h
@@ -21,2 +21,8 @@

+int get_remote_desc (void);
+void set_remote_desc (gdb_fildes_t);
+int get_listen_desc (void);
+
+extern int remote_debug;
+
 int gdb_connected (void);
@@ -32,3 +38,4 @@ int putpkt_binary (char *buf, int len);
 int putpkt_notif (char *buf);
-int getpkt (char *buf);
+int getpkt (gdb_fildes_t,char *buf);
+int handle_accept_event (int err, gdb_client_data client_data);
 void remote_prepare (const char *name);

diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index 127cd3840bc..cda5b8fa420 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -38,2 +38,3 @@
 #include <vector>
+#include <string>
 #include "gdbsupport/common-inferior.h"
@@ -138,2 +139,4 @@ static unsigned char *mem_buf;

+static int attach_count;
+
 /* A sub-class of 'struct notif_event' for stop, holding information
@@ -155,5 +158,80 @@ static struct btrace_config current_btrace_conf;

-/* The client remote protocol state. */
+static struct multi_client_states client_states;
+
+multi_client_states&
+get_client_states (void)
+{
+  return client_states;
+}
+
+static void handle_status (char *);
+
+/* The wait state of a client */
+enum pending_types
+{none_pending, pending_waitee, pending_cont_waiter};
+static const char *pending_types_str[] = {"not-waiting","waitee","waiter"};
+
+/* The notification state of a nonstop client */
+enum nonstop_pending_types
+{no_notifier_pending, pending_notifier, pending_notified};
+static const char *nonstop_pending_types_str[] = {"", "notifier", "notified"};
+
+/* The packet type of a client packet */
+static const char *packet_types_str[] =
+{"other", "vContc", "vConts","vContr","vContt","vRun", "vAttach", "Hg", "g_or_m", "Detach",
+ "vStopped"};
+static const char *waitkind_str[] =
+{"exited", "stopped", "signalled", "loaded", "forked", "vforked", "execed",
+ "vfork-done", "syscall-entry", "syscall-exit", "spurious", "ignore",
+ "no-history", "not-resumed", "thread-created", "thread-exited", ""};
+
+static const char *displaced_step_str[] =
+    {"", "displaced-stepping", "displaced-stepping?"};
+
+
+/* Add a new client state for FD or return FD's client state if found */
+
+client_state *
+multi_client_states::set_client_state (gdb_fildes_t fd)
+{
+/* States:
+ * fd = -1 add initial client state
+ * fd =  F add client state for fd F
+ */
+
+  client_state *csidx, *cs;
+
+  /* add/return initial client state */
+  if (fd == -1)
+    {
+      if (client_states.cs.empty())
+	{
+	  client_states.cs[fd] = new (client_state);
+	  client_states.cs[fd]->file_desc = fd;
+	}
+      client_states.current_cs = client_states.cs[fd];
+      return client_states.cs[fd];
+    }
+
+  /* add/return client state for fd F */
+
+  std::map<gdb_fildes_t,client_state*>::iterator it;
+  it = client_states.cs.find(fd);
+  if (it != client_states.cs.end())
+    {
+      it->second->copy_status (*client_states.current_cs);
+      client_states.current_cs = it->second;
+      return it->second;
+    }
+  else
+    csidx = client_states.cs.rbegin()->second;
+
+  /* add client state S for fd F */
+  client_states.cs[fd] = cs = new client_state (fd, csidx);
+  cs->file_desc = fd;
+  client_states.set_current_client (cs);
+  return cs;
+}

-static client_state g_client_state;
+/* Return the current client state. */

@@ -162,4 +240,849 @@ get_client_state ()
 {
-  client_state &cs = g_client_state;
-  return cs;
+  return client_states.get_client_state ();
+}
+
+client_state::client_state (gdb_fildes_t fd, client_state *csidx)
+{
+  file_desc = fd;
+  cont_thread = null_ptid;
+  general_thread = null_ptid;
+  new_general_thread = null_ptid;
+  multi_process = csidx->multi_process;
+  report_fork_events = csidx->report_fork_events;
+  report_vfork_events = csidx->report_vfork_events;
+  report_exec_events = csidx->report_exec_events;
+  report_thread_events = csidx->report_thread_events;
+  swbreak_feature = csidx->swbreak_feature;
+  hwbreak_feature = csidx->hwbreak_feature;
+  vCont_supported = csidx->vCont_supported;
+  disable_randomization = csidx->disable_randomization;
+  last_status = csidx->last_status;
+  last_ptid = null_ptid;
+  noack_mode = csidx->noack_mode;
+  transport_is_reliable = csidx->transport_is_reliable;
+
+  packet_type = other_packet;
+  last_packet_type = other_packet;
+  own_buf = (char*) xmalloc (PBUFSIZ + 1);
+}
+
+
+void
+client_state::copy_status (client_state &csidx)
+{
+  cont_thread = csidx.cont_thread;
+  general_thread = csidx.general_thread;
+  last_status = csidx.last_status;
+  last_ptid = csidx.last_ptid;
+}
+
+
+/* Are client CS1 and client CS2 attached to the same process?
+   i.e. do they share the same server_state? */
+
+static int
+attached_to_same_proc (client_state * cs1, client_state * cs2)
+{
+  return cs1->file_desc != -1 && cs2->file_desc != -1
+    && cs1 != cs2 /*&& cs1->ss == cs2->ss*/;
+}
+
+
+/* Dump the client state list for debugging purposes */
+
+static void dump_stop_queue(const char*,const char*);
+static void
+dump_client_state (const char *function, const char *comment)
+{
+  client_state *save_cs = client_states.current_cs;
+  client_state *cs;
+
+  if (! debug_threads)
+    return;
+
+  dump_stop_queue (function, comment);
+  debug_printf ("** client state at %s: %s\n", function, comment);
+  std::map<gdb_fildes_t,client_state*>::iterator it;
+
+  for (it = client_states.cs.begin(); it != client_states.cs.end(); ++it)
+    {
+      cs = it->second;
+      const char *last_status_kind;
+      struct regcache *regcache = NULL;
+      CORE_ADDR pc = 0;
+      int sc = 0;
+      static const char *syscall_state[] = {"","ignoring-syscalls"};
+
+      client_states.current_cs = cs;
+      if (cs->file_desc == -1)
+	continue;
+
+      if (cs->last_status.kind < (sizeof (waitkind_str) / sizeof (void*)))
+	last_status_kind = waitkind_str[cs->last_status.kind];
+      else
+	last_status_kind = "";
+      if (current_thread != NULL)
+	regcache = current_thread->regcache_data;
+      if (regcache != NULL)
+	pc = (*the_target->read_pc) (regcache);
+ if (current_thread && current_process()->syscalls_to_catch.size() > 0)
+	if (current_process()->syscalls_to_catch[0] == NO_SYSCALL)
+	  sc = 1;
+      debug_printf ("  %c%d %s pc=%#lx %s %s %s %s %s %s %s\n",
+		    (save_cs == cs) ? '*' : ' ',
+		    cs->file_desc,
+		    target_pid_to_str (cs->general_thread),
+		    (long unsigned)pc,
+		    packet_types_str[cs->packet_type],
+		    packet_types_str[cs->last_packet_type],
+		    pending_types_str[cs->pending],
+		    nonstop_pending_types_str[cs->nonstop_pending],
+		    last_status_kind,
+		    displaced_step_str[cs->displaced_step],
+		    syscall_state[sc]);
+    }
+  client_states.current_cs = save_cs;
+}
+
+
+/* Add another client for PID to the 1 server -> N client list. */
+
+static int
+add_client_by_pid (int pid)
+{
+  client_state &cs = get_client_state();
+  client_state *matched_cs;
+  std::map<gdb_fildes_t,client_state*>::iterator it;
+
+  for (it = client_states.cs.begin(); it != client_states.cs.end(); ++it)
+    {
+      matched_cs = it->second;
+      if (cs.file_desc != matched_cs->file_desc
+	  && matched_cs->general_thread.pid() == pid)
+	{
+	  cs.multi_process = matched_cs->multi_process;
+	  cs.general_thread = matched_cs->general_thread;
+	  attach_count += 1;
+	  return 1;
+	}
+    }
+  return 0;
+}
+
+
+/* Return the first client state; skipping the initial state for fd -1 */
+
+static client_state*
+get_first_client (void)
+{
+  client_state *cs;
+  std::map<gdb_fildes_t,client_state*>::iterator it;
+
+  for (it = client_states.cs.begin(); it != client_states.cs.end(); ++it)
+    {
+      cs = it->second;
+      if (cs->file_desc > 0 /* && cs->attached_to_client == 1 */)
+       return cs;
+    }
+  return client_states.cs.begin()->second;
+}
+
+static gdb_fildes_t
+get_first_client_fd (void)
+{
+  client_state *cs = get_first_client ();
+  return cs->file_desc;
+}
+
+
+/* Do we have multiple clients? */
+
+bool
+have_multiple_clients (gdb_fildes_t fd)
+{
+  std::map<gdb_fildes_t,client_state*>::iterator it;
+
+  dump_client_state(__FUNCTION__, "");
+  for (it = client_states.cs.begin(); it != client_states.cs.end(); ++it)
+    {
+      if (it->first == fd)
+	return (attach_count > 0);
+    }
+  return false;
+}
+
+
+/* Remove the client state corresponding to fd */
+
+client_state&
+multi_client_states::delete_client_state (gdb_fildes_t fd)
+{
+  client_state *cs, *previous_cs = NULL;
+  std::map<gdb_fildes_t,client_state*>::iterator it;
+
+  for (it = client_states.cs.begin(); it != client_states.cs.end(); ++it)
+    {
+      cs = it->second;
+
+      if (it->first == fd)
+	{
+	  client_states.current_cs = previous_cs;
+	  free (cs->own_buf);
+	  delete (cs);
+	  attach_count -= 1;
+	  it = client_states.cs.erase (it);
+	}
+      previous_cs = cs;
+    }
+  return *client_states.current_cs;
+}
+
+
+/* Return the packet type for client packet CS */
+
+static packet_types
+get_packet_type (client_state & cs)
+{
+  char own_packet;
+  struct process_info *process;
+  client_state *csi;
+  packet_types this_packet_type = other_packet;
+
+  if (cs.own_buf)
+    own_packet = cs.own_buf[0];
+  else
+    return other_packet;
+
+  std::map<gdb_fildes_t,client_state*>::iterator it;
+
+  switch (own_packet)
+    {
+    case 'v':
+      if ((strncmp (cs.own_buf, "vCont;c", 7) == 0))
+	this_packet_type = vContc;
+      else if ((strncmp (cs.own_buf, "vCont;s", 7) == 0))
+	this_packet_type = vConts;
+      else if ((strncmp (cs.own_buf, "vCont;r", 7) == 0))
+	this_packet_type = vContr;
+      else if ((strncmp (cs.own_buf, "vCont;t", 7) == 0))
+	this_packet_type = vContt;
+      else if (strncmp (cs.own_buf, "vRun", 4) == 0)
+	this_packet_type = vRun;
+      else if (strncmp (cs.own_buf, "vAttach", 4) == 0)
+	this_packet_type = vAttach;
+      else if (strncmp (cs.own_buf, "vStopped", 4) == 0)
+	this_packet_type = vStopped;
+
+      switch (this_packet_type)
+	{
+	case vContc:
+	  for (it = client_states.cs.begin(); it != client_states.cs.end(); ++it)
+	    {
+	      csi = it->second;
+ if (attached_to_same_proc (&cs, csi) && csi->syscall_op != NO_SYSCALL)
+		{
+		  process = current_process ();
+		  process->syscalls_to_catch[0] = csi->syscall_op;
+		  csi->syscall_op = NO_SYSCALL;
+		}
+	    }
+	  break;
+	case vConts:
+	  if (cs.displaced_step == maybe_displaced_step)
+	    cs.displaced_step = in_displaced_step;
+	  __attribute__ ((fallthrough));
+	case vContr:
+	  for (it = client_states.cs.begin(); it != client_states.cs.end(); ++it)
+	    {
+	      csi = it->second;
+	      /* an in process next can be thrown off by a syscall,
+		 so turn off and on around the next */
+	      if (attached_to_same_proc (&cs, csi) && csi->catch_syscalls)
+		{
+		  process = current_process ();
+		  int vec_value = process->syscalls_to_catch[0];
+		  if (vec_value != NO_SYSCALL)
+		    csi->syscall_op = vec_value;
+		  process->syscalls_to_catch[0] = NO_SYSCALL;
+		}
+	    }
+	}
+      return this_packet_type;
+
+    case 'H':
+      if (cs.own_buf[1] == 'g')
+	{
+	  client_states.current_cs->new_general_thread =
+	    read_ptid (&cs.own_buf[2], NULL);
+	  return Hg;
+	}
+      break;
+    case 'D':
+      return Detach;
+    case 'G':
+      if (non_stop && cs.displaced_step == not_displaced_step)
+	cs.displaced_step = maybe_displaced_step;
+      else if (cs.displaced_step == in_displaced_step)
+	cs.displaced_step = not_displaced_step;
+      __attribute__ ((fallthrough));
+    case 'm':
+    case 'M':
+    case 'g':
+      return g_or_m;
+    };
+  return other_packet;
+}
+
+
+static void queue_stop_reply_callback (thread_info *thread);
+
+static void
+vstop_notif_reply (struct notif_event *event, char *own_buf)
+{
+  struct vstop_notif *vstop = (struct vstop_notif *) event;
+
+  prepare_resume_reply (own_buf, vstop->ptid, &vstop->status);
+}
+
+struct notif_server notif_stop =
+{
+  "vStopped", "Stop", {}, vstop_notif_reply,
+};
+
+/* Belatedly reply to client CS, which is waiting on a packet reply. */
+
+static void
+resolve_waiter (client_state *cs, client_state *waitee_cs)
+{
+  enum packet_types this_packet_type =
+ (enum packet_types)((cs->packet_type) ? cs->packet_type : cs->last_packet_type);
+  client_state *save_client_state;
+
+  save_client_state = client_states.current_cs;
+  client_states.set_client_state (cs->file_desc);
+  if (debug_threads)
+    {
+      debug_printf ("%s:%d fd=%d %s %s", __FUNCTION__, __LINE__,
+		    cs->file_desc, packet_types_str[this_packet_type],
+		    pending_types_str[cs->pending]);
+      if (waitee_cs)
+ 	debug_printf (" fd=%d %s %s", waitee_cs->file_desc,
+		      packet_types_str[waitee_cs->packet_type],
+		      pending_types_str[waitee_cs->pending]);
+      debug_printf ("\n");
+    }
+  switch (this_packet_type)
+    {
+    case vContc:
+      {
+	strcpy (cs->own_buf, "?");
+	if (non_stop)
+	  {
+	    debug_printf ("vContc non stop\n");
+	    if (cs->last_status.kind != TARGET_WAITKIND_EXITED)
+	      cs->nonstop_pending = pending_notifier;
+	  }
+	else if (cs->last_status.kind != TARGET_WAITKIND_EXITED)
+	  {
+	    debug_printf ("vContc all stop not exit\n");
+	    /* reply to vContc with a status */
+	    strcpy (cs->own_buf, "?");
+	    handle_status (cs->own_buf);
+	    putpkt (cs->own_buf);
+	  }
+	else if (cs->last_status.kind == TARGET_WAITKIND_EXITED)
+	  {
+	    debug_printf ("vContc all stop exit\n");
+	    /* Is this the same process we are waiting on? */
+	    if (cs->last_cont_ptid.pid() != cs->last_ptid.pid())
+	      {
+		prepare_resume_reply (cs->own_buf, cs->last_cont_ptid,
+				      &cs->last_cont_status);
+		putpkt (cs->own_buf);
+	      }
+	    else
+	      putpkt (waitee_cs->own_buf);
+	  }
+	break;
+      }
+    case vConts:
+      {
+	if (non_stop)
+	  {
+	    debug_printf ("vConts non stop exit\n");
+	    if (cs->last_status.kind != TARGET_WAITKIND_EXITED)
+	      cs->nonstop_pending = pending_notifier;
+	  }
+	else if (cs->last_status.kind != TARGET_WAITKIND_EXITED)
+	  {
+ debug_printf ("vConts all stop not exit %s %s\n", waitee_cs->own_buf, strstr (waitee_cs->own_buf, "T05syscall"));
+	    putpkt (waitee_cs->own_buf);
+	  }
+	else if (cs->last_status.kind == TARGET_WAITKIND_EXITED)
+	  {
+	    debug_printf ("vConts all stop exit\n");
+	    putpkt (waitee_cs->own_buf);
+	  }
+	break;
+      }
+    case vContt:
+      {
+	if (!waitee_cs ||
+	    (waitee_cs->last_status.kind != TARGET_WAITKIND_EXITED
+		&& waitee_cs && waitee_cs->last_status.kind != TARGET_WAITKIND_STOPPED))
+	  {
+	    char *notif_buf, *out_buf;
+	    notif_buf = (char*) alloca (PBUFSIZ + 1);
+	    write_ok (notif_buf);
+	    putpkt (notif_buf);
+
+	    for_each_thread (queue_stop_reply_callback);
+	    notif_write_event (&notif_stop, notif_buf);
+	    out_buf = (char*) alloca (strlen (notif_buf) + 8);
+	    xsnprintf (out_buf, PBUFSIZ, "%s:", notif_stop.notif_name);
+	    strcat (out_buf, notif_buf);
+	    if (debug_threads)
+	      debug_printf ("%s:%d Notifying fd=%d\n", __FUNCTION__, __LINE__,
+			    cs->file_desc);
+	    putpkt_notif (out_buf);
+	    if (debug_threads)
+	      debug_printf ("%s:%d %s\n", __FUNCTION__, __LINE__, out_buf);
+	  }
+	break;
+      }
+    case vRun:
+      {
+	strcpy (cs->own_buf, "OK");
+	putpkt (cs->own_buf);
+	break;
+      }
+    default:
+      break;
+    };
+
+  client_states.set_client_state (save_client_state->file_desc);
+}
+
+
+/* What is the wait status of the clients sharing CURRENT_CS's server state? */
+
+static void
+analyze_group (client_state & current_cs, int *have_waitee)
+{
+  client_state *csi;
+
+  *have_waitee = 0;
+  std::map<gdb_fildes_t,client_state*>::iterator it;
+
+  for (it = client_states.cs.begin(); it != client_states.cs.end(); ++it)
+    {
+      csi = it->second;
+      if (attached_to_same_proc (&current_cs, csi))
+	{
+	  if (csi->pending == pending_waitee || csi->pending == none_pending)
+	    *have_waitee += 1;
+	}
+    }
+}
+
+
+/* Determine the state of client CS with respect to other clients sharing
+   the same server process */
+
+static int
+setup_multiplexing (client_state & current_cs)
+{
+  /***
+      If we have two independent clients 4 and 7 and 4 is the current
+      client with a vConts packet then we change both client's state
+      BEFORE
+      packet_type last_packet_type pending last_status_.kind
+      4 vConts    vConts	   not-waiting stopped
+      7 other     vAttach	   not-waiting stopped
+      AFTER
+      4 vConts    vConts	   step-waiter stopped
+      7 other     vAttach	   waitee      stopped
+
+ If we have two dependent clients 4 and 7 and 4 is the current client with a + vContc packet then we change nothing here. process_serial_event will handle + the vContc then do_multiplexing will decide to either continue with client 4
+      or wakeup and continue with client 7.
+      4 vContc vConts waitee stopped
+      7 vContc vContc waiter stopped
+  ***/
+
+  client_state *same_pid_cs = NULL;
+
+  if (!have_multiple_clients(current_cs.file_desc))
+    return 1;
+
+  dump_client_state (__FUNCTION__, "");
+
+  if (current_cs.packet_type == vContc || current_cs.packet_type == vConts)
+    current_cs.nonstop_pending = none_pending;
+
+  std::map<gdb_fildes_t,client_state*>::iterator it;
+
+  for (it = client_states.cs.begin(); it != client_states.cs.end(); ++it)
+    {
+      same_pid_cs = it->second;
+      if (! attached_to_same_proc (&current_cs, same_pid_cs))
+	continue;
+
+      switch (same_pid_cs->pending)
+	{
+	case /* same_pid_cs->pending */ none_pending:
+	  /* found a client that has nothing pending */
+	  /* The current_cs vContc will wait; found same_pid_cs will proceed */
+	  switch (current_cs.packet_type)
+	    {
+	    case /* current_cs.packet_type */ vContc:
+		/* Don't make a syscall client a waitee as a waiter cannot
+		   depend on receiving a reply from it */
+	      if (current_cs.last_packet_type != vRun
+		  && ! same_pid_cs->catch_syscalls)
+		{
+		  current_cs.pending = pending_cont_waiter;
+		  same_pid_cs->pending = pending_waitee;
+		}
+	      break;
+	    }
+	  break;
+	case /* same_pid_cs->pending */ pending_cont_waiter:
+	  switch (current_cs.packet_type)
+	    {
+	      /* Current client is continuing and found another waiter client */
+	    case /* current_cs.packet_type */ vContc:
+	      {
+		int have_waitee;
+		analyze_group (current_cs, &have_waitee);
+		dump_client_state (__FUNCTION__, "waitee/waiter switch");
+		/* Don't want to deadlock on everyone waiting */
+		if (current_cs.pending == pending_waitee && have_waitee)
+		  current_cs.pending = pending_cont_waiter;
+	      }
+	      break;
+	    }
+	  break;
+	case /* same_pid_cs->pending */ pending_waitee:
+	  switch (current_cs.packet_type)
+	    {
+	      /* Current client is continuing and found another waitee client */
+	    case /* current_cs.packet_type */ vContc:
+	      {
+		/* Don't make a syscall client a waitee as a waiter cannot
+		   depend on receiving a reply from it */
+		if (!(non_stop
+		      && current_cs.catch_syscalls
+		      && it->second->packet_type == vContc))
+		  current_cs.pending = pending_cont_waiter;
+	      }
+	      break;
+	    }
+	  debug_printf ("%s DBG %s catching=%d\n", __FUNCTION__,
+			target_waitstatus_to_string (&get_client_state().last_status).c_str(),
+			current_cs.catch_syscalls);
+	} /* switch same_pid_cs->pending */
+    } /* for same_pid_cs */
+
+
+  switch (current_cs.pending)
+    {
+    case pending_cont_waiter:
+      switch (current_cs.last_packet_type)
+        {
+	case vRun:
+	  break;
+	default:
+	  /* Current client is continuing and waiting so don't reply to this
+	     packet now; it will be replied to later in do_multiplexing */
+	  if (current_cs.packet_type == vContc)
+	    {
+	      debug_printf ("%s DBG %s %d\n", __FUNCTION__,
+			target_waitstatus_to_string (&get_client_state().last_status).c_str(),
+			current_cs.catch_syscalls);
+	      dump_client_state (__FUNCTION__, "* waiting");
+	      current_cs.last_cont_ptid = current_cs.general_thread;
+	      current_cs.last_cont_status = current_cs.last_status;
+	      return 0;		/* Reply to packet later */
+	    }
+	} /* switch current_cs.last_packet_type */
+      __attribute__ ((fallthrough));
+    default:
+      if (current_cs.catch_syscalls && current_cs.packet_type == vContc
+	  && current_cs.displaced_step == in_displaced_step)
+	{
+	  dump_client_state (__FUNCTION__, "* waiting");
+	  return 0;
+	}
+      dump_client_state (__FUNCTION__, "* continuing");
+      return 1;			/* Reply to packet now */
+    } /* switch current_cs.pending */
+}
+
+
+/* Send a notification to a pending client.  Called via the
+   handle_target_event notification mechanism.  This is a non-stop
+   variation of do_multiplexing */
+
+int
+notify_clients (char *buffer, int have_first_notify)
+{
+  int save_client_fd = client_states.current_cs->file_desc;
+  client_state *save_client_cs = client_states.current_cs;
+  char *okay_buf = (char*) alloca (4);
+  int have_syscall = 0;
+
+  dump_client_state (__FUNCTION__, "");
+
+  write_ok (okay_buf);
+
+  std::map<gdb_fildes_t,client_state*>::iterator it;
+  for (it = client_states.cs.begin(); it != client_states.cs.end(); ++it)
+    {
+      /* Is this a client attached to the same process? */
+      if (! attached_to_same_proc (client_states.current_cs, it->second))
+	continue;
+
+      switch (save_client_cs->last_status.kind)
+      {
+	case TARGET_WAITKIND_SYSCALL_ENTRY:
+	case TARGET_WAITKIND_SYSCALL_RETURN:
+	  have_syscall = 1;
+	  break;
+	case TARGET_WAITKIND_EXITED:
+	  client_states.set_client_state (it->second->file_desc);
+	  putpkt (okay_buf);
+	  putpkt_notif (buffer);
+	  client_states.set_client_state (save_client_fd);
+	  return 1;
+        case TARGET_WAITKIND_STOPPED:
+	  /* Have more than 1 notify so also send to shadow client */
+	  if (save_client_cs->packet_type == vStopped)
+		it->second->nonstop_pending = pending_notifier;
+	  __attribute__ ((fallthrough));
+	default:
+	  /* Only syscall clients need to see a syscall packet */
+	  if (save_client_cs->catch_syscalls)
+	    {
+	      client_states.set_client_state (it->second->file_desc);
+	      return 1;
+	    }
+      }
+
+ /* syscall continue was erroneously caught by by a non syscall client */
+      if (save_client_cs->packet_type == other_packet
+	  && it->second->catch_syscalls)
+	return 0;
+
+      /* client wants the notification */
+      if (it->second->nonstop_pending == pending_notifier)
+	{
+	  /* Also send the notification to the attached client */
+	  client_states.set_client_state (it->second->file_desc);
+	  if (debug_threads)
+ debug_printf ("%s:%d Notifying fd=%d\n", __FUNCTION__, __LINE__, it->second->file_desc);
+	  /* This is the first notification */
+	  if (have_first_notify
+	      && (/*is_waiter (it->second)
+		  ||*/ (save_client_cs->packet_type != vStopped
+		      && it->second->nonstop_pending == pending_notifier)))
+	    {
+	      putpkt (okay_buf);
+	      putpkt_notif (buffer);
+	    }
+	  else			/* This is another notification in the group */
+	    {
+	      if (it->second->packet_type == vStopped)
+		putpkt (buffer);
+	    }
+	  client_states.set_client_state (save_client_fd);
+	  it->second->pending = none_pending;
+	  it->second->nonstop_pending = pending_notified;
+	}
+    }
+  if (have_syscall && !save_client_cs->catch_syscalls)
+    return 0;
+  else
+    return 1;
+}
+
+
+/* Resolve the state of client WAITEE_CS with respect to other clients
+   sharing the same server process */
+
+static int
+do_multiplexing (client_state & current_cs)
+{
+  /***
+ If we have two dependent clients 4 and 7 and 4 is the current client with a + vContc packet then if the handling of vContc resulted in hitting a client 7
+  breakpoint then we wakeup client 7.
+
+  BEFORE
+  packet_type last_packet_type pending last_status_.kind
+  4 vContc    vConts		waitee stopped
+  7 vContc    vContc		waiter stopped
+  AFTER
+  4 vContc    vConts		waiter stopped
+  7 vContc    vContc		waitee stopped
+
+  Otherwise we continue with client 4.
+  4 vContc    vConts		waitee stopped
+  7 vContc    vContc		waiter stopped
+  ***/
+
+  client_state *same_pid_cs = NULL;
+  int make_waitee_a_waiter = 0;
+
+  if (!have_multiple_clients(current_cs.file_desc))
+    return 1;
+
+  switch (current_cs.packet_type)
+    {
+    case other_packet:
+      break;
+    case vContt:
+      if (current_cs.last_packet_type == vAttach ||
+	  current_cs.last_packet_type == Hg)
+	{
+	  resolve_waiter (&current_cs, NULL);
+	  dump_client_state (__FUNCTION__, "resolved vContt");
+	  current_cs.last_packet_type = other_packet;
+	  return 0;
+	}
+      __attribute__ ((fallthrough));
+    default:
+          current_cs.last_packet_type = current_cs.packet_type;
+    }
+
+  dump_client_state (__FUNCTION__, "");
+
+ /* We are only interested in a current nonwaiting client that is continuing */
+  switch (current_cs.packet_type)
+    {
+    case vContc:
+    case vContt:
+    case vConts:
+    case Detach:
+      if (current_cs.pending == pending_waitee)
+	break;
+      __attribute__ ((fallthrough));
+    default:
+      dump_client_state (__FUNCTION__, "no action taken");
+      return 1;
+    }
+
+  std::map<gdb_fildes_t,client_state*>::iterator it;
+
+  for (it = client_states.cs.begin(); it != client_states.cs.end(); ++it)
+    {
+      same_pid_cs = it->second;
+      client_state *waiter_cs = NULL;
+      int want_waiter_resolved = 0;
+
+      /* Is this a waiting continuing client attached to the cs process */
+      if (! attached_to_same_proc (&current_cs, same_pid_cs)
+	  || (same_pid_cs->pending != pending_cont_waiter)
+	  || (same_pid_cs->packet_type != vContc))
+	continue;
+
+      switch (current_cs.packet_type)
+	{
+	case vConts:
+	  current_cs.last_cont_ptid = current_cs.general_thread;
+	  current_cs.last_cont_status = current_cs.last_status;
+	} /* switch current_cs.packet_type */
+
+      switch (get_client_state().last_status.kind)
+	{
+	case TARGET_WAITKIND_EXITED:
+	  waiter_cs = client_states.set_client_state (same_pid_cs->file_desc);
+	  make_waitee_a_waiter = 1;
+	  want_waiter_resolved = 1;
+	  break;
+	case TARGET_WAITKIND_SYSCALL_ENTRY:
+	case TARGET_WAITKIND_SYSCALL_RETURN:
+	  /* current client continued and got a syscall
+	     which another client was waiting for */
+	  if (same_pid_cs->catch_syscalls
+	      || (current_cs.catch_syscalls && current_cs.packet_type == Detach))
+	    {
+	      waiter_cs = client_states.set_client_state (same_pid_cs->file_desc);
+	      make_waitee_a_waiter = 1;
+	      want_waiter_resolved = 1;
+	    }
+	  break;
+        case TARGET_WAITKIND_STOPPED:
+	  /* we want only syscalls but got something else */
+	  if (current_cs.catch_syscalls)
+	    {
+	      waiter_cs = client_states.set_client_state (same_pid_cs->file_desc);
+	      make_waitee_a_waiter = 1;
+	      want_waiter_resolved = 1;
+	    }
+	  else if (get_first_thread () != NULL)
+	    {
+	      struct regcache *regcache;
+	      CORE_ADDR point_addr = 0;
+
+	      if ((*the_target->stopped_by_watchpoint)())
+		point_addr = (*the_target->stopped_data_address) ();
+	      else
+		{
+		  regcache = current_thread->regcache_data;
+		  point_addr = (*the_target->read_pc) (regcache);
+		}
+	      if (point_addr)
+		{
+		  waiter_cs = client_states.set_client_state (same_pid_cs->file_desc);
+		  /* Does found client have a breakpoint at PC? */
+		  want_waiter_resolved = gdb_breakpoint_here (point_addr);
+		}
+	      else if (current_cs.packet_type == vConts
+		       && current_cs.last_status.kind == TARGET_WAITKIND_STOPPED)
+		{
+		  /* there is no target->stopped_by_single_step so just assume that */
+		  waiter_cs = client_states.set_client_state (same_pid_cs->file_desc);
+		  want_waiter_resolved = 1;
+		}
+	    }
+	  break;
+	default:
+	  /* we want only syscalls but got something else */
+	  if (current_cs.catch_syscalls)
+	    {
+	      waiter_cs = client_states.set_client_state (same_pid_cs->file_desc);
+	      make_waitee_a_waiter = 1;
+	      want_waiter_resolved = 1;
+	    }
+	} /* switch last_status->kind */
+
+      if (want_waiter_resolved)
+	{
+	  /* We reached the other client's breakpoint, belatedly reply */
+	  waiter_cs->copy_status (current_cs);
+	  resolve_waiter (waiter_cs, &current_cs);
+	  current_cs.pending = none_pending;
+	  waiter_cs->pending = pending_waitee;
+	}
+ client_state *lcs = client_states.set_client_state (current_cs.file_desc);
+      current_cs = *lcs;
+    } /* for (same_pid_cs  */
+
+  if (make_waitee_a_waiter && current_cs.packet_type == vContc)
+    {
+ /* Switch the current and waiting clients. The packet will be replied
+	 to in a later do_multiplexing call */
+      current_cs.pending = pending_cont_waiter;
+      dump_client_state (__FUNCTION__, "* waiting");
+      current_cs.last_cont_ptid = current_cs.general_thread;
+      current_cs.last_cont_status = current_cs.last_status;
+      return 0;
+    }
+  else
+    {
+      dump_client_state (__FUNCTION__, "* continuing");
+      return 1;
+    }
 }
@@ -167,2 +1090,4 @@ get_client_state ()

+/**********************************/
+
 /* Put a stop reply to the stop reply queue.  */
@@ -208,2 +1133,3 @@ discard_queued_stop_replies (ptid_t ptid)

+/*
 static void
@@ -215,2 +1141,3 @@ vstop_notif_reply (struct notif_event *event, char *own_buf)
 }
+*/

@@ -249,2 +1176,3 @@ in_queued_stop_replies (ptid_t ptid)

+/*
 struct notif_server notif_stop =
@@ -253,2 +1181,3 @@ struct notif_server notif_stop =
 };
+*/

@@ -644,2 +1573,3 @@ handle_general_set (char *own_buf)
 	    process->syscalls_to_catch.push_back (ANY_SYSCALL);
+	  client_states.current_cs->catch_syscalls = 1;
 	}
@@ -744,2 +1674,9 @@ handle_general_set (char *own_buf)

+      if (client_states.cs.size() > 2 && (non_stop ^ req))
+	{
+	  write_enn (own_buf);
+	  return;
+	}
+
+
       req_str = req ? "non-stop" : "all-stop";
@@ -959,2 +1896,4 @@ monitor_show_help (void)
   monitor_output ("\n");
+  monitor_output ("  client status\n");
+  monitor_output ("    Display status of multiple clients\n");
   monitor_output ("  exit\n");
@@ -1197,2 +2136,9 @@ handle_detach (char *own_buf)

+  if (have_multiple_clients(cs.file_desc) && cs.catch_syscalls)
+    {
+      write_ok (cs.own_buf);
+      putpkt (cs.own_buf);
+      cs = client_states.delete_client_state(cs.file_desc);
+      return;
+    }
   if (cs.multi_process)
@@ -1401,2 +2347,39 @@ handle_monitor_command (char *mon, char *own_buf)
     }
+  else if (strcmp (mon, "client status") == 0)
+    {
+      client_state *csidx;
+      char *result = (char*)"";
+      char *old_result = NULL;
+      std::map<gdb_fildes_t,client_state*>::iterator it;
+
+ for (it = client_states.cs.begin(); it != client_states.cs.end(); ++it)
+	{
+	  csidx = it->second;
+	  if (csidx->file_desc > 0)
+	    {
+	      int asr;
+	      old_result = result;
+	      asr = asprintf (&result, "%s fd %d %s %s is %s", result,
+			csidx->file_desc, target_pid_to_str (csidx->general_thread),
+ csidx->last_status.kind != TARGET_WAITKIND_IGNORE ? waitkind_str[csidx->last_status.kind] : "",
+			pending_types_str[csidx->pending]);
+	      if (strlen (old_result))
+		free (old_result);
+	      if (asr)
+		old_result = result;
+	      if (csidx->packet_type)
+		asr = asprintf (&result, "%s with a current %s request\n", result,
+			  packet_types_str[csidx->packet_type]);
+	      else
+		asr = asprintf (&result, "%s\n", result);
+	      if (strlen (old_result))
+		free (old_result);
+	    }
+	}
+      if (strlen (result) > 0)
+	{
+	  monitor_output (result);
+	  free (result);
+	}
+    }
   else if (strcmp (mon, "set debug-file") == 0)
@@ -1660,2 +2643,3 @@ handle_qxfer_threads_worker (thread_info *thread, struct buffer *buffer)
 {
+  client_state &cs = get_client_state ();
   ptid_t ptid = ptid_of (thread);
@@ -1669,2 +2653,7 @@ handle_qxfer_threads_worker (thread_info *thread, struct buffer *buffer)

+  /* TODO an attached client does not know about multiple inferiors */
+  if (ptid.pid() != cs.general_thread.pid()
+      && have_multiple_clients (cs.file_desc))
+    return;
+
   write_ptid (ptid_s, ptid);
@@ -2640,3 +3629,3 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)

-      strcpy (own_buf, process->attached ? "1" : "0");
+ strcpy (own_buf, process->attached || have_multiple_clients (cs.file_desc) ? "1" : "0");
       return;
@@ -2915,5 +3904,23 @@ handle_v_attach (char *own_buf)
   int pid;
+  int status;

   pid = strtol (own_buf + 8, NULL, 16);
-  if (pid != 0 && attach_inferior (pid) == 0)
+  status = add_client_by_pid(pid);
+  if (status < 0)
+    {
+ sprintf (own_buf, "E.Attached client non-stop/all-stop mode does not match the running client.");
+      return 0;
+    }
+  else if (status == 1)
+    {
+      if (! non_stop)
+	{
+	  strcpy (own_buf, "?");
+	  prepare_resume_reply (own_buf, cs.general_thread, &cs.last_status);
+	}
+      else
+	write_ok (own_buf);
+      return 1;
+    }
+  else if (pid != 0 && attach_inferior (pid) == 0)
     {
@@ -3340,4 +4347,4 @@ handle_status (char *own_buf)

-      /* The first is sent immediatly.  OK is sent if there is no
-	 stopped thread, which is the same handling of the vStopped
+      /* The first is sent immediately.  OK is sent if there is no
+	 stopped thread, which is the same handling as the vStopped
 	 packet (by design).  */
@@ -3575,2 +4582,5 @@ captured_main (int argc, char *argv[])
   int was_running;
+
+  int remote_desc = get_remote_desc();
+  client_states.set_client_state (remote_desc);
   bool selftest = false;
@@ -4039,9 +5049,20 @@ process_serial_event (void)
   response_needed = 0;
-  packet_len = getpkt (cs.own_buf);
+  packet_len = getpkt (cs.file_desc, cs.own_buf);
   if (packet_len <= 0)
     {
+      gdb_fildes_t fdt;
       remote_close ();
+      fdt = get_first_client_fd ();
       /* Force an event loop break.  */
-      return -1;
+      if (fdt > 0)
+	{
+	  client_state * lcs = client_states.set_client_state (fdt);
+	  cs = *lcs;
+	  set_remote_desc (fdt);
+	  packet_len = getpkt (cs.file_desc, cs.own_buf);
+	}
+      else
+	return -1;
     }
+
   response_needed = 1;
@@ -4049,2 +5070,8 @@ process_serial_event (void)
   char ch = cs.own_buf[0];
+
+  cs.packet_type = get_packet_type (cs);
+
+  if (! setup_multiplexing (cs))
+    return 0;
+
   switch (ch)
@@ -4371,2 +5398,5 @@ process_serial_event (void)

+  if (! do_multiplexing (get_client_state()))
+    return 0;
+
   if (new_packet_len != -1)
@@ -4394,2 +5424,3 @@ handle_serial_event (int err, gdb_client_data client_data)
   if (process_serial_event () < 0)
+
     return -1;
@@ -4403,2 +5434,27 @@ handle_serial_event (int err, gdb_client_data client_data)

+/* Dump stop notifications */
+
+static void
+dump_stop_queue (const char *function, const char *comment)
+{
+  if (!debug_threads)
+    return;
+  if (notif_stop.queue.size() == 0)
+    return;
+  debug_printf ("** stop queue at %s: %s\n", function, comment);
+  /**/
+  std::list<notif_event *>::iterator iter, next, end;
+  end = notif_stop.queue.end ();
+  for (iter = notif_stop.queue.begin (); iter != end; iter = next)
+    {
+      struct vstop_notif *vsn = (struct vstop_notif*)*iter;
+      next = iter;
+      ++next;
+
+ debug_printf ("%d %s\n", vsn->ptid.pid(), target_waitstatus_to_string (((const target_waitstatus*)&(vsn->status))).c_str());
+    }
+  /**/
+}
+
+
 /* Push a stop notification on the notification queue.  */
@@ -4421,3 +5477,5 @@ handle_target_event (int err, gdb_client_data client_data)
 {
+  client_state *csi, *save_csi;
   client_state &cs = get_client_state ();
+
   if (debug_threads)
@@ -4428,2 +5486,7 @@ handle_target_event (int err, gdb_client_data client_data)

+  if (debug_threads)
+ debug_printf ("%s fd=%d queue=%d %s\n", __FUNCTION__, client_states.current_cs->file_desc, (int)notif_stop.queue.size(),
+		  waitkind_str[cs.last_status.kind]);
+  save_csi = client_states.current_cs;
+
   if (cs.last_status.kind == TARGET_WAITKIND_NO_RESUMED)
@@ -4439,2 +5502,21 @@ handle_target_event (int err, gdb_client_data client_data)

+      if ((cs.last_status.kind == TARGET_WAITKIND_SYSCALL_ENTRY
+	  || cs.last_status.kind == TARGET_WAITKIND_SYSCALL_RETURN)
+	  && have_multiple_clients (cs.file_desc))
+	{
+	  std::map<gdb_fildes_t,client_state*>::iterator it;
+
+	  for (it = client_states.cs.begin(); it != client_states.cs.end(); ++it)
+	    {
+	      csi = it->second;
+	      if (attached_to_same_proc (save_csi, csi)
+		  && csi->catch_syscalls)
+		{
+		  save_csi = client_states.current_cs;
+		  csi->nonstop_pending = pending_notifier;
+		  break;
+		}
+	    }
+	}
+
       if (cs.last_status.kind == TARGET_WAITKIND_EXITED
@@ -4495,2 +5577,3 @@ handle_target_event (int err, gdb_client_data client_data)
   set_desired_thread ();
+  client_states.set_client_state (save_csi->file_desc);

diff --git a/gdb/gdbserver/server.h b/gdb/gdbserver/server.h
index 520969453ac..c9f307bbb76 100644
--- a/gdb/gdbserver/server.h
+++ b/gdb/gdbserver/server.h
@@ -63,2 +63,4 @@ int vsnprintf(char *str, size_t size, const char *format, va_list ap);
 #include "gdbsupport/environ.h"
+#include <list>
+#include <map>

@@ -94,2 +96,3 @@ 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 int notify_clients (char *buffer, int first);

@@ -123,2 +126,5 @@ extern int in_queued_stop_replies (ptid_t ptid);

+/* Trace child processes created via fork or clone.  */
+#define NO_SYSCALL (-3)
+
 /* After fork_inferior has been called, we need to adjust a few
@@ -135,2 +141,13 @@ extern unsigned long signal_pid;

+enum displaced_step_type
+  { not_displaced_step, in_displaced_step, maybe_displaced_step };
+typedef enum displaced_step_type displaced_step_type;
+
+/* The packet type of a client packet */
+
+enum packet_types
+ { other_packet, vContc, vConts, vContr, vContt, vRun, vAttach, Hg, g_or_m, Detach, vStopped };
+typedef enum packet_types packet_types;
+
+
 /* Description of the client remote protocol state for the currently
@@ -143,2 +160,15 @@ struct client_state
   {}
+  client_state (gdb_fildes_t fd, client_state *csidx);
+
+  gdb_fildes_t file_desc;
+  int packet_type = other_packet;
+  int last_packet_type = other_packet;
+  displaced_step_type displaced_step = not_displaced_step; // +
+  int pending = 0;
+  int nonstop_pending = 0;
+  int catch_syscalls = 0;
+  int syscall_op = NO_SYSCALL;
+  ptid_t last_cont_ptid;
+  ptid_t new_general_thread;
+  struct target_waitstatus last_cont_status;

@@ -150,6 +180,6 @@ struct client_state
      multi-process), or a specific thread ptid_t.  */
-  ptid_t cont_thread;
+  ptid_t cont_thread = null_ptid; // +

   /* The thread set with an `Hg' packet.  */
-  ptid_t general_thread;
+  ptid_t general_thread = null_ptid; // +

@@ -185,4 +215,4 @@ struct client_state
   /* Last status reported to GDB.  */
-  struct target_waitstatus last_status;
-  ptid_t last_ptid;
+  struct target_waitstatus last_status; // +
+  ptid_t last_ptid; // +

@@ -200,5 +230,23 @@ struct client_state

+  void copy_status (client_state &csidx);
+};
+
+
+struct multi_client_states
+{
+public:
+  std::map<gdb_fildes_t,client_state*> cs;
+  client_state *current_cs;
+
+  client_state & get_client_state (void) { return *current_cs; }
+  void set_current_client (client_state *cs) { current_cs = cs; }
+
+  client_state * set_client_state (gdb_fildes_t);
+  void free_client_state (client_state *cs);
+  client_state & delete_client_state (gdb_fildes_t fd);
 };

-client_state &get_client_state ();
+client_state & get_client_state ();
+struct multi_client_states & get_client_states (void);
+bool have_multiple_clients (gdb_fildes_t fd);

diff --git a/gdb/remote.c b/gdb/remote.c
index ae06c4ba791..89af5083dca 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -2630,3 +2630,3 @@ remote_target::set_syscall_catchpoint (int pid, bool needed, int any_count,
 	 characters for a sysno.  If the resulting packet size is too
-	 big, fallback on the non-selective packet.  */
+	 big, fallback on the non selective packet.  */
       const int maxpktsz = strlen ("QCatchSyscalls:1") + n_sysno * 9 + 1;

diff --git a/gdb/testsuite/gdb.server/multi-client.exp b/gdb/testsuite/gdb.server/multi-client.exp
new file mode 100644
index 00000000000..9b06b012824
--- /dev/null
+++ b/gdb/testsuite/gdb.server/multi-client.exp
@@ -0,0 +1,440 @@
+# Copyright (C) 2015, 2016, 2017, 2018 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+exp_internal 0
+
+load_lib gdbserver-support.exp
+load_lib prelink-support.exp
+
+if {[skip_gdbserver_tests]} {
+    return 0
+}
+
+set test "multi-client"
+global srcdir
+global subdir
+global gdbserver_gdbport
+global gdbserver_reconnect_p
+global server_spawn_id
+global target_pid
+global pgrep_spawn_id
+# set testpath [standard_output_file $testfile]
+
+verbose "srcdir=$srcdir subdir=$subdir [standard_output_file $test]"
+set testfile catch-syscall
+set srcfile ../gdb.base/catch-syscall.c
+set testpath [standard_output_file $testfile]
+set gdbserver_reconnect_p 1
+
+set testfile2 strace-threads
+set srcfile2 strace-threads.c
+set testpath2 [standard_output_file $testfile2]
+
+if { [prepare_for_testing $test $testfile $srcfile debug] } {
+    return -1
+}
+if {[build_executable $test $testfile $srcfile debug] == -1} {
+    return -1
+}
+
+if {[build_executable $test $testfile2 $srcfile2 {debug additional_flags=-lpthread}] == -1} {
+    return -1
+}
+
+set use_gdb_stub 0
+set strace_spawn_id ""
+
+proc start_client { TESTPATH NONSTOP COMMENT } {
+    global test
+    global GDB
+    global MULTI_GDBFLAGS
+    global gdbserver_gdbport
+    global gdb_spawn_id
+    global inferior_spawn_id
+    global srcdir
+    global subdir
+    gdb_exit
+    gdb_start
+    gdb_load $TESTPATH
+    gdb_reinitialize_dir $srcdir/$subdir
+    gdb_test_no_output "set remotetimeout 600"
+    gdb_test_no_output "set non-stop $NONSTOP"
+    gdb_test "target extended-remote $gdbserver_gdbport"
+ gdb_test_no_output "set remote exec-file $TESTPATH" "set remote exec-file"
+    return $gdb_spawn_id
+}
+
+proc start_strace { ATTACH_FLAGS } {
+    global test
+    set STRACE [auto_execok strace]
+    verbose "strace is $STRACE $ATTACH_FLAGS"
+    set res [remote_spawn host "$STRACE $ATTACH_FLAGS"]
+    expect_before {
+        -i $res " invalid option "  {
+            xfail "no gdb remote protocol strace"
+            return 0
+        }
+        eof exit
+    }
+    sleep 5
+    if { $res < 0 || $res == "" } {
+        perror "$test Spawning $STRACE failed."
+        return 1
+    }
+    return $res
+}
+
+proc get_target_pid { target_exec } {
+    global test
+    global target_pid
+    global pgrep_spawn_id
+
+    set target_pid -1
+    set res [remote_spawn host "pgrep $target_exec"]
+    sleep 1
+    expect {
+	-i $res -re {[0-9][0-9]*} { set target_pid $expect_out(buffer) }
+    }
+    if { $target_pid < 0 } {
+	perror "$test Failed to get pid of target process. $expect_out(buffer)"
+	return 1
+    }
+    close -i $res
+    set pgrep_spawn_id $res
+    return $target_pid
+}
+
+proc gdb_command { COMMAND RESPONSE COMMENT } {
+    global gdb_spawn_id
+    global gdb_prompt
+    global test
+    set ok 0
+    send -i $gdb_spawn_id "$COMMAND\n"
+    if { [string length $RESPONSE] > 0 } {
+	gdb_test_multiple "" "$COMMENT" {
+	    -i $gdb_spawn_id -re "$RESPONSE.*\r\n$gdb_prompt $" {
+		incr ok;
+	    }
+	}
+	if {$ok == 1} {
+	    pass "$COMMENT"
+	}
+    }
+}
+
+set clean_idx 0
+
+proc cleanup_server_and_clients { } {
+    global strace_spawn_id
+    global server_spawn_id
+    global target_pid
+    global gdb_spawn_id
+    global inferior_spawn_id
+    global pgrep_spawn_id
+    global clean_idx
+
+    incr clean_idx
+    close_gdbserver
+    catch {exec kill -KILL [exp_pid -i $strace_spawn_id]}
+    catch {exec kill -KILL [exp_pid -i $server_spawn_id]}
+    catch {exec kill -KILL [exp_pid -i $gdb_spawn_id]}
+    catch {exec kill -KILL [exp_pid -i $inferior_spawn_id]}
+    catch {exec kill -KILL [exp_pid -i $pgrep_spawn_id]}
+    if { $target_pid > 1 } {catch {exec kill -KILL $target_pid}}
+    gdb_exit
+}
+
+foreach NONSTOP { off on } {
+
+#-------------------------------------------------------
+
+verbose "##### Test coordination of a gdb client and an strace client"
+
+set res [gdbserver_start "--multi" ""]
+sleep 2
+set gdbserver_protocol [lindex $res 0]
+set gdbserver_gdbport [lindex $res 1]
+
+start_client $testpath "$NONSTOP" "non-stop $NONSTOP"
+
+gdb_command "b main" "Breakpoint 1 at" "gdb/strace/0-thr/$NONSTOP b main"
+
+gdb_command "b 76" "Breakpoint 2 at" "gdb/strace/0-thr/$NONSTOP b _exit"
+
+gdb_command "run" "Breakpoint 1," "gdb/strace/0-thr/$NONSTOP hit b main"
+
+set target_pid [get_target_pid catch-syscall]
+
+set strace_spawn_id [start_strace "-f -G $gdbserver_gdbport -o [standard_output_file strace-$NONSTOP.out] -p $target_pid"]
+if { $strace_spawn_id == 0 || $strace_spawn_id == ""} {
+    return 0
+}
+
+#		close (-1);
+gdb_command "next" "" ""
+gdb_command "next" "close" "gdb/strace/0-thr/$NONSTOP next close"
+
+#		chroot (".");
+gdb_command "next" "chroot" "gdb/strace/0-thr/$NONSTOP next chroot"
+
+#		pipe (fd);
+gdb_command "next" "pipe" "gdb/strace/0-thr/$NONSTOP next pipe"
+
+#		write (fd[1], buf1, sizeof (buf1));
+gdb_command "next" "write" "gdb/strace/0-thr/$NONSTOP next write"
+
+#		read (fd[0], buf2, sizeof (buf2));
+gdb_command "next" "read" "gdb/strace/0-thr/$NONSTOP next read"
+
+#		vfork ();
+gdb_command "next" "vfork" "gdb/strace/0-thr/$NONSTOP next vfork"
+
+gdb_command "continue" "_exit" "gdb/strace/0-thr/$NONSTOP test exited"
+
+gdb_command "quit" "" ""
+close -i $gdb_spawn_id
+
+set infile [open [standard_output_file strace-$NONSTOP.out] r]
+set ok 0
+while { [gets $infile line] >= 0 } {
+    switch -regexp $line {
+	".*close\\(.*=" { incr ok }
+	".*chroot\\(.*=" { incr ok }
+	".*pipe\\(.*=" { incr ok }
+	".*write\\(.*=" { incr ok }
+	".*read\\(.*=" { incr ok }
+	".*vfork.*=" { incr ok }
+    }
+}
+switch $ok {
+    6 { pass  "gdb/strace/0-thr/$NONSTOP ($ok)" }
+    0 { unresolved "gdb/strace/0-thr/$NONSTOP ($ok)" }
+    default { fail "gdb/strace/0-thr/$NONSTOP ($ok)" }
+}
+
+cleanup_server_and_clients
+sleep 5
+
+close $infile
+
+# ----------------------------------------------------------------------
+
+verbose "##### Test coordination of a gdb client and an strace client with a threaded test"
+
+set res [gdbserver_start "--multi" ""]
+set gdbserver_protocol [lindex $res 0]
+set gdbserver_gdbport [lindex $res 1]
+
+start_client $testpath2 "$NONSTOP" "non-stop $NONSTOP"
+
+gdb_command "b main" "Breakpoint 1 at" "gdb/strace/1-thr/$NONSTOP b main"
+
+gdb_command "run" "Breakpoint 1," "gdb/strace/1-thr/$NONSTOP hit b main"
+
+set target_pid [get_target_pid $testfile2]
+
+set strace_spawn_id [start_strace "-f -e \!futex -G $gdbserver_gdbport -o [standard_output_file strace-gdb-$NONSTOP.out] -p $target_pid"]
+if { $strace_spawn_id == 0 || $strace_spawn_id == ""} {
+    return 0
+}
+
+gdb_command "b thread_worker" "Breakpoint 2 at" "gdb/strace/1-thr/$NONSTOP b thread_worker" +gdb_command "continue" "Breakpoint 2" "gdb/strace/1-thr/$NONSTOP hit b thread_worker"
+
+# TODO needed, a reliable way to choose the desired thread
+gdb_command "thread 2" "" ""
+
+#		close (-1);
+set bp_location [gdb_get_line_number "close" "$srcfile2"]
+gdb_command "b $srcfile2:$bp_location" "Breakpoint 3" "gdb/strace/1-thr/$NONSTOP b close" +gdb_command "continue" "Breakpoint 3" "gdb/strace/1-thr/$NONSTOP hit b close"
+gdb_command "next" "chroot" "gdb/strace/1-thr/$NONSTOP next close"
+
+#		chroot (".");
+gdb_command "next" "pipe" "gdb/strace/1-thr/$NONSTOP next chroot"
+
+#		pipe (fd);
+gdb_command "next" "write" "gdb/strace/1-thr/$NONSTOP next pipe"
+
+#		write (fd[1])
+gdb_command "next" "read" "gdb/strace/1-thr/$NONSTOP next write"
+
+#		read (fd[1])
+gdb_command "next" "syscall" "gdb/strace/1-thr/$NONSTOP next read"
+
+#		syscall (unknown_syscall)
+gdb_command "next" "\}" "gdb/strace/1-thr/$NONSTOP next syscall"
+
+gdb_command "quit" "" ""
+
+set infile [open [standard_output_file strace-gdb-$NONSTOP.out] r]
+set ok 0
+set have_clone 0
+while { [gets $infile line] >= 0 } {
+    switch -regexp $line {
+	".*clone\\(" { set have_clone 1 }
+	".*close\\(" { if { $have_clone } {incr ok;verbose "close"} }
+	".*chroot\\(" { if { $have_clone } {incr ok;verbose "chroot"} }
+	".*pipe\\(" { if { $have_clone } {incr ok;verbose "pipe"} }
+	".*write\\(" { if { $have_clone } {incr ok;verbose "write"} }
+	".*read\\(" { if { $have_clone } {incr ok;verbose "read"} }
+ ".*syscall_[abcdefx0123456789]*\\(" { if { $have_clone } {incr ok;verbose "syscall"} }
+    }
+}
+switch $ok {
+    4 -
+    5 { xfail "gdb/strace/1-thr/$NONSTOP ($ok)" }
+    6 { pass  "gdb/strace/1-thr/$NONSTOP ($ok)" }
+    0 { unresolved "gdb/strace/1-thr/$NONSTOP ($ok)" }
+    default { fail "gdb/strace/1-thr/$NONSTOP ($ok)" }
+}
+
+cleanup_server_and_clients
+sleep 1
+
+# ----------------------------------------------------------------------
+
+verbose "##### Test an strace client with the threaded test"
+
+set res [gdbserver_start "--once" "$testpath2"]
+set gdbserver_protocol [lindex $res 0]
+set gdbserver_gdbport [lindex $res 1]
+sleep 1
+
+set target_pid [get_target_pid $testfile2]
+set strace_spawn_id [start_strace "-f -G $gdbserver_gdbport -o [standard_output_file strace-thr-$NONSTOP.out]"]
+if { $strace_spawn_id == 0 || $strace_spawn_id == ""} {
+     return 0
+}
+set ok 0
+expect {
+    -i $server_spawn_id -re {exited} { incr ok }
+}
+expect {
+    -i $strace_spawn_id -re {detached} { incr ok }
+}
+switch $ok {
+    2 { pass "gdbserver/strace" }
+    0 { fail "gdbserver/strace ($ok)" }
+}
+
+close -i $strace_spawn_id
+close $infile
+set infile [open [standard_output_file strace-thr-$NONSTOP.out] r]
+set ok 0
+set have_clone 0
+while { [gets $infile line] >= 0 } {
+    switch -regexp $line {
+	".*clone\\(" { set have_clone 1 }
+	".*close\\(" { if { $have_clone } {incr ok}}
+	".*chroot\\(" { if { $have_clone } {incr ok} }
+	".*pipe\\(" { if { $have_clone } {incr ok} }
+	".*write\\(" { if { $have_clone } {incr ok} }
+	".*read\\(" { if { $have_clone } {incr ok} }
+	".*syscall_[abcdefx0123456789]*\\(" { if { $have_clone } {incr ok} }
+    }
+}
+switch $ok {
+    6 { pass  "strace/0-thr/$NONSTOP ($ok)" }
+    0 { unresolved "strace/0-thr/$NONSTOP ($ok)" }
+    default { fail "strace/0-thr/$NONSTOP ($ok)" }
+}
+
+cleanup_server_and_clients
+close $infile
+sleep 5
+
+# ----------------------------------------------------------------------
+
+verbose "##### Test coordination of a gdb client and an strace client with a multithreaded test"
+
+set res [gdbserver_start "--multi" ""]
+set gdbserver_protocol [lindex $res 0]
+set gdbserver_gdbport [lindex $res 1]
+
+start_client $testpath2 "$NONSTOP" "multithread non-stop $NONSTOP"
+
+gdb_command "b main" "Breakpoint 1 at" "gdb/strace/2-thr/$NONSTOP b main"
+
+gdb_command "run 2" "Breakpoint 1," "gdb/strace/2-thr/$NONSTOP hit b main"
+
+set target_pid [get_target_pid $testfile2]
+
+set strace_spawn_id [start_strace "-f -e \!futex -G $gdbserver_gdbport -o [standard_output_file strace-gdb-multi-$NONSTOP.out] -p $target_pid"]
+if { $strace_spawn_id == 0 || $strace_spawn_id == ""} {
+    return 0
+}
+
+set bp_location [gdb_get_line_number "close" "$srcfile2"]
+gdb_command "b $srcfile2:$bp_location" "Breakpoint 2" "gdb/strace/2-thr/$NONSTOP b close" +gdb_command "continue" "Breakpoint 2" "gdb/strace/2-thr/$NONSTOP hit b close"
+# TODO needed, a reliable way to choose the desired thread
+gdb_command "info thread" "" ""
+gdb_command "thread 2" "close" "gdb/strace/2-thr/$NONSTOP"
+
+#		close (-1);
+if { $NONSTOP == off} {
+    gdb_command "next" "" ""
+}
+gdb_command "next" "chroot" "gdb/strace/2-thr/$NONSTOP next close"
+
+#		chroot (".");
+gdb_command "next" "pipe" "gdb/strace/2-thr/$NONSTOP next chroot"
+
+#		pipe (fd);
+gdb_command "next" "write" "gdb/strace/2-thr/$NONSTOP next pipe"
+
+#		write (fd[1])
+gdb_command "next" "read" "gdb/strace/2-thr/$NONSTOP next write"
+
+#		read (fd[1])
+gdb_command "next" "syscall" "gdb/strace/2-thr/$NONSTOP next read"
+
+#		syscall (unknown_syscall)
+gdb_command "next" "\}" "gdb/strace/2-thr/$NONSTOP next syscall"
+
+gdb_command "thread 3" "Switching to thread" "gdb/strace/2-thr/$NONSTOP switch threads"
+
+# gdb_command "continue" "exited normally" "exited $NONSTOP"
+gdb_command "continue" "" ""
+
+gdb_command "quit" "" ""
+
+set infile [open [standard_output_file strace-gdb-multi-$NONSTOP.out] r]
+set ok 0
+set have_clone 0
+while { [gets $infile line] >= 0 } {
+    switch -regexp $line {
+	".*clone\\(" { set have_clone 1 }
+	".*close\\(" { if { $have_clone } {incr ok} }
+	".*chroot\\(" { if { $have_clone } {incr ok} }
+	".*pipe\\(" { if { $have_clone } {incr ok} }
+	".*write\\(" { if { $have_clone } {incr ok} }
+	".*read\\(" { if { $have_clone } {incr ok} }
+	".*syscall_[abcdefx0123456789]*\\(" { if { $have_clone } {incr ok} }
+    }
+}
+switch $ok {
+    0 { unresolved "gdb/strace/2-thr/$NONSTOP ($ok)" }
+    6 { pass  "gdb/strace/2-thr/$NONSTOP ($ok)" }
+    default { xfail "gdb/strace/2-thr/$NONSTOP ($ok)" }
+}
+
+cleanup_server_and_clients
+close $infile
+sleep 5
+
+# END foreach NONSTOP
+}


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]