Stan Cox
2018-11-22 14:06:09 UTC
Looking for comments, thoughts, and suggestions on this (large) patch
that adds 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...
* Goal and Approach
** Use cases
*Purpose
Allow multiple clients that use the gdb remote protocol to multiplex
the same gdbserver inferior
*Often mentioned: gdb client and an strace client
* strace reports syscalls in gdb's execution flow
* stays synchronized with the gdb client
* gdbserver strace support added
* made into a separate strace backend
* ptrace support also made into a separate backend
* gdbserver / strace
* Josh Stone added all-stop strace gdbserver inline
* We created a gdbserver dispatch table backend and moved ptrace to a
backend
* Goal is to make use of this functionality coexisting with gdbserver
debugging the same process
* this patch is currently under review
* example with standalone 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)
...
close(3) = 0
openat(AT_FDCWD, 0x7ffff7ffede0, O_RDONLY|O_CLOEXEC) = 3
read(3,
"\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0Pb\0\0\0\0\0\0"..., 832)
= 832
close(-1) = -1 EBADF (Bad file descriptor)
chroot(0x400990) = -1 EPERM (Operation not
permitted)
pipe([3, 4]) = 0
write(4, "a\0", 2) = 2
read(3, "a\0", 2) = 2
vfork( <unfinished ...>
[pid 19429] +++ exited with 0 +++
<... vfork resumed> ) = 19429
syscall_0x75bcd15(0x7fffffffd6e0, 0x2, 0x7ffff78beae8, 0x7ffff7bb2d80,
0x7ffff7bb2d80, 0x7fffffffd7f8) = -1 ENOSYS (Function not implemented)
--- stopped by 255 ---
/work/scox/strace/scoxsrc/strace: Process 19421 detached
* Add a multiple client/server data structure
* int packet_type;
* identifies the type of packet for this and previous packet
* other_packet, vContc, vConts, vContr, vContt, vRun, vAttach, Hg,
g_or_m, Detach, vStopped
* displaced_step_type
* int pending describes the client state
* none_pending, pending_waitee, pending_cont_waiter
* int nonstop_pending describes the nonstop state
* no_notifier_pending, pending_notifier, pending_notified
* int catch_syscalls;
* int syscall_op
* used to save and restore process->syscalls_to_catch for
situations where that is needed temporarily
* NO_SYSCALL, ANY_SYSCALL, XXX_SYSCALL
* Connecting another client
* Perhaps this piece could be a separate patch?
* event-loop.c
* wait_for_event :: now adds a select in case another client tries
to connect. add a new client if so.
* handle_file_event :: Set the client state if we are attaching to a
client already managed by gdbserver
* remote-utils.c
* get_remote_desc, get_listen_desc :: allow fds to be accessed
outside remote-utils.c
* handle_accept_event :: Set the default client_state to
the new fd and don't close the listen_desc
* write_prim, read_prim, readchar, putpkt, getpkt :: add a parameter
for fd
* clients_states mapping from file descriptor to client_state
* "indexed" by file descriptor of the client
* There is always a current client
* add_client_by_pid add another client state coordinating the same pid
* e.g. client 1 does 'run program' and client 2 is an strace that
attaches to the resulting pid
* additions for multiplexing clients
* clients have a state, an input, and are managed more or less as a FSM
* states: not-waiting waitee waiter step-waiter
* inputs: other, vContc, vConts, vContt, vStopped, vRun, vAttach, Hg,
g_or_m
* One client is always current and one is waiting.
* For a syscall the syscall client becomes the current client
* While the gdb client waits
* For a continue, next, or step the gdb client potentially waits if
a syscall is reached
* setup_multiplexing is invoked before the request case
in server.c::process serial event
* Check current state, packet input and other client's state
* If current client was doing continue or next, make it wait
* while the strace client handles the syscall
* e.g. current client 1 "not waiting" "vContc" and client 2 "not
waiting"
becomes client 1 "waiter" "vContc" and current client 2 "waitee"
* states:
packet type client1 state client2 state new c1 state new c2 state
vContc * none pending cont waiter waitee
vConts waitee none pending step waiter waitee
vContc waitee step waiter cont waiter waitee
vConts waitee step waiter step waiter waitee
vContc waitee cont waiter cont waiter *
vConts waitee cont waiter step waiter *
vContc * waitee cont waiter *
vConts * waitee step waiter *
* do_multiplexing is invoked after the request case
* complement of setup_multiplexing
* resolves the waiting client
* do_multiplexing handles most of the client switch situations
* A state machine that determines if a client's waiting should be
resolved
* do_multiplexing: Resolve the state of all the clients for a given
inferior after the packet handling switch
* Handles the case where the current client is continuing: has a
vContc or vConts packet
* Typically for an all stop client
* e.g. when a gdb client does a continue and a syscall is hit,
* resolve_waiter does the work of replying to a waiting client
* handle_status gets the status which is passed to the waiting client
* The non stop case just sets the client state to pending_notifier
* The remainder of the non stop work is done in notify_clients
* e.g. client 1 "waiter" "vContc"
* and current client 2 "waitee" "vContc"
* becomes client 1 "not waiting"
* and current client 2 "not waiting"
* if client 2 reached the breakpoint that client 1 was waiting for
* resolve_waiter wakes up a waiting client
* for all_stop mode: call handle_status and putpkt the result
* for non stop mode: create a vstop_notif and call notif_push
* process_event->handle_file_event->handle_target_event
->push_stop_notification->notif_push
* notify_clients notifies an attached client
* invoked by notif_push when it outputs a notification
* determines if the notification should be sent to another client
* notif_write_event->notify_clients
* example
stop queue at notify_clients: first
21487 status->kind = entered syscall
client state at notify_clients: first
*4 LWP 21487.21513 pc=0x7ffff78e39f7 vContc vContc waitee
syscall-entry
10 LWP 21487.21513 pc=0x7ffff78e39f7 vContc vContc waiter notifier
syscall-entry
notify_clients:791 after switch first=1 have_syscall=1 notifier=0/1
stop queue at notify_clients: after switch
21487 status->kind = entered syscall
client state at notify_clients: after switch
4 LWP 21487.21513 pc=0x7ffff78e39f7 vContc vContc waitee syscall-entry
*10 LWP 21487.21513 pc=0x7ffff78e39f7 vContc vContc waiter notifier
syscall-entry
notify_clients:801 Notifying fd=10 first and (same_is_waiter or
save_is_not_vstopped
putpkt/10 ("$OK#9a"); [noack mode]
putpkt/10 ("%Stop:T05syscall_entry:16;06:f06e7ff7ff7f0*
;07:b86e7ff7ff7f0* ;10:f7398ef7ff7f0*
;thread:p5409.53ef;thread:p5409.53ef;core:7;#27"); [notif]
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
multi-client.exp | 440
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
strace-threads.c | 76 +++++++++++++++++++
event-loop.c | 44 ++++-
linux-low.c | 3 +
notif.c | 4 +-
remote-utils.c | 97 ++++++----
remote-utils.h | 7 +-
server.c | 1113
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
server.h | 58 +++++-
diff --git a/event-loop.c b/event-loop.c
index a3310a05c8b..eddbbcdfa5f 100644
--- a/event-loop.c
+++ b/event-loop.c
@@ -19,6 +19,7 @@
/* Based on src/gdb/event-loop.c. */
#include "server.h"
+#include "remote-utils.h"
#include <sys/types.h>
#include "gdb_sys_time.h"
@@ -375,7 +376,11 @@ delete_file_handler (gdb_fildes_t fd)
;
prev_ptr->next_file = file_ptr->next_file;
}
- 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);
}
/* Handle the given event by calling the procedure associated to the
@@ -415,6 +420,12 @@ handle_file_event (gdb_fildes_t event_file_desc)
/* If there was a match, then call the handler. */
if (mask != 0)
{
+ /* 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,
file_ptr->client_data) < 0)
return -1;
@@ -455,6 +466,37 @@ wait_for_event (void)
file_handler *file_ptr;
int num_found = 0;
+ /* 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. */
fflush (stdout);
fflush (stderr);
diff --git a/linux-low.c b/linux-low.c
index 701f3e863c0..7f78b7a39fd 100644
--- a/linux-low.c
+++ b/linux-low.c
@@ -3084,6 +3084,9 @@ gdb_catch_this_syscall_p (struct lwp_info
*event_child)
if (proc->syscalls_to_catch.empty ())
return 0;
+ if (proc->syscalls_to_catch[0] == NO_SYSCALL)
+ return 0;
+
if (proc->syscalls_to_catch[0] == ANY_SYSCALL)
return 1;
diff --git a/notif.c b/notif.c
index 5ff7079123f..42ef793f255 100644
--- a/notif.c
+++ b/notif.c
@@ -67,6 +67,7 @@ notif_write_event (struct notif_server *notif, char
*own_buf)
= QUEUE_peek (notif_event_p, notif->queue);
notif->write (event, own_buf);
+ notify_clients (own_buf, 0);
}
else
write_ok (own_buf);
@@ -150,7 +151,8 @@ notif_push (struct notif_server *np, struct
notif_event *new_event)
p += strlen (p);
np->write (new_event, p);
- putpkt_notif (buf);
+ if (notify_clients (buf, 1))
+ putpkt_notif (buf);
}
}
diff --git a/remote-utils.c b/remote-utils.c
index 9199a9c7add..15a8b5cf337 100644
--- a/remote-utils.c
+++ b/remote-utils.c
@@ -94,7 +94,7 @@ enum {
Either NOT_SCHEDULED or the callback id. */
static int readchar_callback = NOT_SCHEDULED;
-static int readchar (void);
+static int readchar (gdb_fildes_t);
static void reset_readchar (void);
static void reschedule (void);
@@ -123,6 +123,25 @@ extern int debug_threads;
# define write(fd, buf, len) send (fd, (char *) buf, len, 0)
#endif
+
+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
gdb_connected (void)
{
@@ -151,7 +170,7 @@ enable_async_notification (int fd)
#endif
}
-static int
+int
handle_accept_event (int err, gdb_client_data client_data)
{
struct sockaddr_storage sockaddr;
@@ -164,6 +183,10 @@ handle_accept_event (int err, gdb_client_data
client_data)
if (remote_desc == -1)
perror_with_name ("Accept failed");
+ 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. */
socklen_t tmp = 1;
setsockopt (remote_desc, SOL_SOCKET, SO_KEEPALIVE,
@@ -180,7 +203,8 @@ handle_accept_event (int err, gdb_client_data
client_data)
exits when the remote side dies. */
#endif
- if (run_once)
+ /* Do not close in case another client tries to attach */
+ if (0 && run_once)
{
#ifndef USE_WIN32API
close (listen_desc); /* No longer need this */
@@ -191,7 +215,8 @@ handle_accept_event (int err, gdb_client_data
client_data)
/* Even if !RUN_ONCE no longer notice new connections. Still keep the
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); */
/* Convert IP address to string. */
char orig_host[GDB_NI_MAX_ADDR], orig_port[GDB_NI_MAX_PORT];
@@ -219,7 +244,9 @@ handle_accept_event (int err, gdb_client_data
client_data)
try to send vStopped notifications to GDB. But, don't do that
until GDB as selected all-stop/non-stop, and has queried the
threads' status ('?'). */
- target_async (0);
+
+ /* Do not async in case another client tries to attach */
+ /* target_async (0); */
return 0;
}
@@ -615,12 +642,12 @@ read_ptid (const char *buf, const char **obuf)
This may return less than COUNT. */
static int
-write_prim (const void *buf, int count)
+write_prim (gdb_fildes_t fd, const void *buf, int count)
{
if (remote_connection_is_stdio ())
return write (fileno (stdout), buf, count);
else
- return write (remote_desc, buf, count);
+ return write (fd, buf, count);
}
/* Read COUNT bytes from the client and store in BUF.
@@ -628,12 +655,12 @@ write_prim (const void *buf, int count)
This may return less than COUNT. */
static int
-read_prim (void *buf, int count)
+read_prim (gdb_fildes_t fd, void *buf, int count)
{
if (remote_connection_is_stdio ())
return read (fileno (stdin), buf, count);
else
- return read (remote_desc, buf, count);
+ return read (fd, buf, count);
}
/* Send a packet to the remote machine, with error checking.
@@ -674,7 +701,7 @@ putpkt_binary_1 (char *buf, int cnt, int is_notif)
do
{
- if (write_prim (buf2, p - buf2) != p - buf2)
+ if (write_prim (cs.file_desc, buf2, p - buf2) != p - buf2)
{
perror ("putpkt(write)");
free (buf2);
@@ -687,21 +714,21 @@ putpkt_binary_1 (char *buf, int cnt, int is_notif)
if (remote_debug)
{
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);
}
break;
}
if (remote_debug)
{
- 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);
if (cc < 0)
{
@@ -769,7 +796,7 @@ input_interrupt (int unused)
int cc;
char c = 0;
- cc = read_prim (&c, 1);
+ cc = read_prim (remote_desc, &c, 1);
if (cc == 0)
{
@@ -899,13 +926,13 @@ static unsigned char *readchar_bufp;
/* Returns next char from remote GDB. -1 if error. */
static int
-readchar (void)
+readchar (gdb_fildes_t fd)
{
int ch;
if (readchar_bufcnt == 0)
{
- readchar_bufcnt = read_prim (readchar_buf, sizeof (readchar_buf));
+ readchar_bufcnt = read_prim (fd, readchar_buf, sizeof
(readchar_buf));
if (readchar_bufcnt <= 0)
{
@@ -974,7 +1001,7 @@ reschedule (void)
and store it in BUF. Returns length of packet, or negative if
error. */
int
-getpkt (char *buf)
+getpkt (gdb_fildes_t fd, char *buf)
{
client_state &cs = get_client_state ();
char *bp;
@@ -987,7 +1014,7 @@ getpkt (char *buf)
while (1)
{
- c = readchar ();
+ c = readchar (fd);
/* The '\003' may appear before or after each packet, so
check for an input interrupt. */
@@ -1012,7 +1039,7 @@ getpkt (char *buf)
bp = buf;
while (1)
{
- c = readchar ();
+ c = readchar (fd);
if (c < 0)
return -1;
if (c == '#')
@@ -1022,8 +1049,8 @@ getpkt (char *buf)
}
*bp = 0;
- c1 = fromhex (readchar ());
- c2 = fromhex (readchar ());
+ c1 = fromhex (readchar (fd));
+ c2 = fromhex (readchar (fd));
if (csum == (c1 << 4) + c2)
break;
@@ -1040,7 +1067,7 @@ getpkt (char *buf)
fprintf (stderr, "Bad checksum, sentsum=0x%x, csum=0x%x, buf=%s\n",
(c1 << 4) + c2, csum, buf);
- if (write_prim ("-", 1) != 1)
+ if (write_prim (fd, "-", 1) != 1)
return -1;
}
@@ -1048,11 +1075,11 @@ getpkt (char *buf)
{
if (remote_debug)
{
- 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;
if (remote_debug)
@@ -1065,8 +1092,8 @@ getpkt (char *buf)
{
if (remote_debug)
{
- debug_printf ("getpkt (\"%s\"); [no ack sent] \n", buf);
- debug_flush ();
+ debug_printf ("getpkt/%d (\"%s\"); [no ack sent] \n", fd, buf);
+ fflush (stderr);
}
}
@@ -1082,7 +1109,7 @@ getpkt (char *buf)
while (readchar_bufcnt > 0 && *readchar_bufp == '\003')
{
/* Consume the interrupt character in the buffer. */
- readchar ();
+ readchar (fd);
(*the_target->request_interrupt) ();
}
@@ -1518,7 +1555,7 @@ look_up_one_symbol (const char *name, CORE_ADDR
*addrp, int may_ask_gdb)
return -1;
/* FIXME: Eventually add buffer overflow checking (to getpkt?) */
- len = getpkt (cs.own_buf);
+ len = getpkt (cs.file_desc, cs.own_buf);
if (len < 0)
return -1;
@@ -1556,7 +1593,7 @@ look_up_one_symbol (const char *name, CORE_ADDR
*addrp, int may_ask_gdb)
}
else
break;
- len = getpkt (cs.own_buf);
+ len = getpkt (cs.file_desc, cs.own_buf);
if (len < 0)
return -1;
}
@@ -1614,7 +1651,7 @@ relocate_instruction (CORE_ADDR *to, CORE_ADDR oldloc)
return -1;
/* FIXME: Eventually add buffer overflow checking (to getpkt?) */
- len = getpkt (cs.own_buf);
+ len = getpkt (cs.file_desc, cs.own_buf);
if (len < 0)
return -1;
@@ -1657,7 +1694,7 @@ relocate_instruction (CORE_ADDR *to, CORE_ADDR oldloc)
free (mem_buf);
if (putpkt (cs.own_buf) < 0)
return -1;
- len = getpkt (cs.own_buf);
+ len = getpkt (cs.file_desc, cs.own_buf);
if (len < 0)
return -1;
}
diff --git a/remote-utils.h b/remote-utils.h
index fcd3b3a93de..94a037f0597 100644
--- a/remote-utils.h
+++ b/remote-utils.h
@@ -19,6 +19,10 @@
#ifndef REMOTE_UTILS_H
#define REMOTE_UTILS_H
+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,7 +36,8 @@ char *write_ptid (char *buf, ptid_t ptid);
int putpkt (char *buf);
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);
void remote_open (const char *name);
void remote_close (void);
diff --git a/server.c b/server.c
index 4ec3548d644..14d15c0fb85 100644
--- a/server.c
+++ b/server.c
@@ -36,6 +36,8 @@
#include "dll.h"
#include "hostio.h"
#include <vector>
+#include <map>
+#include <string>
#include "common-inferior.h"
#include "job-control.h"
#include "environ.h"
@@ -135,6 +137,8 @@ int disable_packet_qfThreadInfo;
static unsigned char *mem_buf;
+static int attach_count;
+
/* A sub-class of 'struct notif_event' for stop, holding information
relative to a single stop reply. We keep a queue of these to
push to GDB in non-stop mode. */
@@ -156,18 +160,940 @@ static struct btrace_config current_btrace_conf;
DEFINE_QUEUE_P (notif_event_p);
-/* 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 */
-static client_state g_client_state;
+ 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;
+}
+
+/* Return the current client state. */
client_state &
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()
+ 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", NULL, 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 (¬if_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 (¤t_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 (¤t_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 (¤t_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 (¤t_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, ¤t_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;
+ }
+}
+
+
+/**********************************/
+
/* Put a stop reply to the stop reply queue. */
static void
@@ -210,14 +1136,6 @@ discard_queued_stop_replies (ptid_t ptid)
remove_all_on_match_ptid, &ptid);
}
-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);
-}
-
/* QUEUE_iterate callback helper for in_queued_stop_replies. */
static int
@@ -250,11 +1168,6 @@ in_queued_stop_replies (ptid_t ptid)
in_queued_stop_replies_ptid, &ptid);
}
-struct notif_server notif_stop =
-{
- "vStopped", "Stop", NULL, vstop_notif_reply,
-};
-
static int
target_running (void)
{
@@ -324,8 +1237,6 @@ attach_inferior (int pid)
return 0;
}
-extern int remote_debug;
-
/* Decode a qXfer read request. Return 0 if everything looks OK,
or -1 otherwise. */
@@ -645,6 +1556,7 @@ handle_general_set (char *own_buf)
}
else
process->syscalls_to_catch.push_back (ANY_SYSCALL);
+ client_states.current_cs->catch_syscalls = 1;
}
write_ok (own_buf);
@@ -745,6 +1657,13 @@ handle_general_set (char *own_buf)
return;
}
+ if (client_states.cs.size() > 2 && (non_stop ^ req))
+ {
+ write_enn (own_buf);
+ return;
+ }
+
+
req_str = req ? "non-stop" : "all-stop";
if (start_non_stop (req) != 0)
{
@@ -960,6 +1879,8 @@ monitor_show_help (void)
monitor_output (" Options: all, none");
monitor_output (", timestamp");
monitor_output ("\n");
+ monitor_output (" client status\n");
+ monitor_output (" Display status of multiple clients\n");
monitor_output (" exit\n");
monitor_output (" Quit GDBserver\n");
}
@@ -1198,6 +2119,13 @@ handle_detach (char *own_buf)
process_info *process;
+ 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)
{
/* skip 'D;' */
@@ -1398,6 +2326,43 @@ handle_monitor_command (char *mon, char *own_buf)
write_enn (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, "help") == 0)
monitor_show_help ();
else if (strcmp (mon, "exit") == 0)
@@ -1653,6 +2618,7 @@ handle_qxfer_statictrace (const char *annex,
static void
handle_qxfer_threads_worker (thread_info *thread, struct buffer *buffer)
{
+ client_state &cs = get_client_state ();
ptid_t ptid = ptid_of (thread);
char ptid_s[100];
int core = target_core_of_thread (ptid);
@@ -1662,6 +2628,11 @@ handle_qxfer_threads_worker (thread_info *thread,
struct buffer *buffer)
gdb_byte *handle;
bool handle_status = target_thread_handle (ptid, &handle, &handle_len);
+ /* 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);
buffer_xml_printf (buffer, "<thread id=\"%s\"", ptid_s);
@@ -2635,7 +3606,7 @@ handle_query (char *own_buf, int packet_len, int
*new_packet_len_p)
return;
}
- strcpy (own_buf, process->attached ? "1" : "0");
+ strcpy (own_buf, process->attached || have_multiple_clients
(cs.file_desc) ? "1" : "0");
return;
}
@@ -2910,9 +3881,27 @@ handle_v_attach (char *own_buf)
{
client_state &cs = get_client_state ();
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)
{
/* Don't report shared library events after attaching, even if
some libraries are preloaded. GDB will always poll the
@@ -3336,8 +4325,8 @@ handle_status (char *own_buf)
{
for_each_thread (queue_stop_reply_callback);
- /* 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). */
notif_write_event (¬if_stop, cs.own_buf);
}
@@ -3572,6 +4561,9 @@ captured_main (int argc, char *argv[])
volatile int multi_mode = 0;
volatile int attach = 0;
int was_running;
+
+ int remote_desc = get_remote_desc();
+ client_states.set_client_state (remote_desc);
bool selftest = false;
#if GDB_SELF_TEST
const char *selftest_filter = NULL;
@@ -4036,16 +5028,33 @@ process_serial_event (void)
disable_async_io ();
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;
char ch = cs.own_buf[0];
+
+ cs.packet_type = get_packet_type (cs);
+
+ if (! setup_multiplexing (cs))
+ return 0;
+
switch (ch)
{
case 'q':
@@ -4368,6 +5377,9 @@ process_serial_event (void)
break;
}
+ if (! do_multiplexing (get_client_state()))
+ return 0;
+
if (new_packet_len != -1)
putpkt_binary (cs.own_buf, new_packet_len);
else
@@ -4391,6 +5403,7 @@ handle_serial_event (int err, gdb_client_data
client_data)
/* Really handle it. */
if (process_serial_event () < 0)
+
return -1;
/* Be sure to not change the selected thread behind GDB's back.
@@ -4400,6 +5413,29 @@ handle_serial_event (int err, gdb_client_data
client_data)
return 0;
}
+/* Dump stop notifications */
+
+static void
+dump_stop_queue (const char *function, const char *comment)
+{
+ struct queue_elem_notif_event_p *queue_elem;
+ struct vstop_notif *vstop_notif;
+
+ if (!debug_threads)
+ return;
+ if (notif_stop.queue->head == 0)
+ return;
+ debug_printf ("** stop queue at %s: %s\n", function, comment);
+ for (queue_elem = notif_stop.queue->head;
+ queue_elem != 0;
+ queue_elem = queue_elem->next)
+ {
+ vstop_notif = (struct vstop_notif*)queue_elem->data;
+ debug_printf ("%d %s\n", vstop_notif->ptid.pid(),
target_waitstatus_to_string (&vstop_notif->status).c_str());
+ }
+}
+
+
/* Push a stop notification on the notification queue. */
static void
@@ -4418,13 +5454,20 @@ push_stop_notification (ptid_t ptid, struct
target_waitstatus *status)
int
handle_target_event (int err, gdb_client_data client_data)
{
+ client_state *csi, *save_csi;
client_state &cs = get_client_state ();
+
if (debug_threads)
debug_printf ("handling possible target event\n");
cs.last_ptid = mywait (minus_one_ptid, &cs.last_status,
TARGET_WNOHANG, 1);
+ if (debug_threads)
+ debug_printf ("%s fd=%d queue=%d %s\n", __FUNCTION__,
client_states.current_cs->file_desc, notif_stop.queue->head == NULL ? 0 : 1,
+ waitkind_str[cs.last_status.kind]);
+ save_csi = client_states.current_cs;
+
if (cs.last_status.kind == TARGET_WAITKIND_NO_RESUMED)
{
if (gdb_connected () && report_no_resumed)
@@ -4436,6 +5479,25 @@ handle_target_event (int err, gdb_client_data
client_data)
struct process_info *process = find_process_pid (pid);
int forward_event = !gdb_connected () || process->gdb_detached;
+ 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
|| cs.last_status.kind == TARGET_WAITKIND_SIGNALLED)
{
@@ -4492,6 +5554,7 @@ handle_target_event (int err, gdb_client_data
client_data)
/* Be sure to not change the selected thread behind GDB's back.
Important in the non-stop mode asynchronous protocol. */
set_desired_thread ();
+ client_states.set_client_state (save_csi->file_desc);
return 0;
}
diff --git a/server.h b/server.h
index 5e41e2f595d..f423406aa22 100644
--- a/server.h
+++ b/server.h
@@ -61,6 +61,8 @@ int vsnprintf(char *str, size_t size, const char
*format, va_list ap);
#include "target.h"
#include "mem-break.h"
#include "environ.h"
+#include <list>
+#include <map>
/* Target-specific functions */
@@ -92,6 +94,7 @@ extern void handle_v_requests (char *own_buf, int
packet_len,
int *new_packet_len);
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);
/* Get rid of the currently pending stop replies that match PTID. */
extern void discard_queued_stop_replies (ptid_t ptid);
@@ -121,6 +124,9 @@ extern int in_queued_stop_replies (ptid_t ptid);
/* Definition for any syscall, used for unfiltered syscall reporting. */
#define ANY_SYSCALL (-2)
+/* Trace child processes created via fork or clone. */
+#define NO_SYSCALL (-3)
+
/* After fork_inferior has been called, we need to adjust a few
signals and call startup_inferior to start the inferior and consume
its first events. This is done here. PID is the pid of the new
@@ -133,6 +139,17 @@ extern gdb_environ *get_environ ();
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
connected client. */
@@ -141,6 +158,19 @@ struct client_state
client_state ():
own_buf ((char *) xmalloc (PBUFSIZ + 1))
{}
+ 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;
/* The thread set with an `Hc' packet. `Hc' is deprecated in favor of
`vCont'. Note the multi-process extensions made `vCont' a
@@ -148,10 +178,10 @@ struct client_state
CONT_THREAD can be null_ptid for no `Hc' thread, minus_one_ptid for
resuming all threads of the process (again, `Hc' isn't used for
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; // +
int multi_process = 0;
int report_fork_events = 0;
@@ -183,8 +213,8 @@ struct client_state
int program_signals_p = 0;
/* Last status reported to GDB. */
- struct target_waitstatus last_status;
- ptid_t last_ptid;
+ struct target_waitstatus last_status; // +
+ ptid_t last_ptid; // +
char *own_buf;
@@ -198,9 +228,27 @@ struct client_state
int current_traceframe = -1;
+ 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);
#include "gdbthread.h"
#include "inferiors.h"
diff --git a/multi-client.exp b/multi-client.exp
new file mode 100644
index 00000000000..9b06b012824
--- /dev/null
+++ b/multi-client.exp
@@ -0,0 +1,440 @@
+# Copyright (C) 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
+}
diff --git a/strace-threads.c b/strace-threads.c
new file mode 100644
index 00000000000..050377e1b4e
--- /dev/null
+++ b/strace-threads.c
@@ -0,0 +1,76 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/syscall.h>
+#include <errno.h>
+
+static pthread_barrier_t barrier;
+
+void *thread_worker ();
+
+
+int
+main(int argc, char *argv[])
+{
+ pthread_t *thread;
+ sigset_t mask1;
+ int thread_count;
+
+ if (argc == 1)
+ thread_count = 1;
+ else
+ {
+ thread_count = strtol(argv[1], NULL, 10);
+ if (errno != 0)
+ {
+ perror("strtol");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ thread = alloca (sizeof (pthread_t) * (thread_count + 1));
+ sigemptyset (&mask1);
+ sigaddset (&mask1, SIGUSR1);
+ sigprocmask (SIG_UNBLOCK, &mask1, NULL);
+
+ pthread_barrier_init (&barrier, NULL, thread_count);
+
+ for (int i = 1; i <= thread_count; i++)
+ pthread_create (&thread[i], NULL, & thread_worker, NULL);
+
+ for (int i = 1; i <= thread_count; i++)
+ pthread_join (thread[i], NULL);
+
+ exit (EXIT_SUCCESS);
+}
+
+void*
+thread_worker ()
+{
+ pthread_barrier_wait (&barrier);
+
+ int unknown_syscall = 123456789;
+ int fd[2];
+ char buf1[2] = "a";
+ char buf2[2];
+
+ close (-1);
+
+ chroot (".");
+
+ pipe (fd);
+
+ write (fd[1], buf1, sizeof (buf1));
+
+ read (fd[0], buf2, sizeof (buf2));
+
+ /* Test vfork-event interactions. Child exits immediately.
+ (Plain fork won't work on no-mmu kernel configurations.) */
+ // if (vfork () == 0)
+ // _exit (0);
+
+ syscall (unknown_syscall);
+}
+
that adds 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...
* Goal and Approach
** Use cases
*Purpose
Allow multiple clients that use the gdb remote protocol to multiplex
the same gdbserver inferior
*Often mentioned: gdb client and an strace client
* strace reports syscalls in gdb's execution flow
* stays synchronized with the gdb client
* gdbserver strace support added
* made into a separate strace backend
* ptrace support also made into a separate backend
* gdbserver / strace
* Josh Stone added all-stop strace gdbserver inline
* We created a gdbserver dispatch table backend and moved ptrace to a
backend
* Goal is to make use of this functionality coexisting with gdbserver
debugging the same process
* this patch is currently under review
* example with standalone 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)
...
close(3) = 0
openat(AT_FDCWD, 0x7ffff7ffede0, O_RDONLY|O_CLOEXEC) = 3
read(3,
"\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0Pb\0\0\0\0\0\0"..., 832)
= 832
close(-1) = -1 EBADF (Bad file descriptor)
chroot(0x400990) = -1 EPERM (Operation not
permitted)
pipe([3, 4]) = 0
write(4, "a\0", 2) = 2
read(3, "a\0", 2) = 2
vfork( <unfinished ...>
[pid 19429] +++ exited with 0 +++
<... vfork resumed> ) = 19429
syscall_0x75bcd15(0x7fffffffd6e0, 0x2, 0x7ffff78beae8, 0x7ffff7bb2d80,
0x7ffff7bb2d80, 0x7fffffffd7f8) = -1 ENOSYS (Function not implemented)
--- stopped by 255 ---
/work/scox/strace/scoxsrc/strace: Process 19421 detached
* Add a multiple client/server data structure
* int packet_type;
* identifies the type of packet for this and previous packet
* other_packet, vContc, vConts, vContr, vContt, vRun, vAttach, Hg,
g_or_m, Detach, vStopped
* displaced_step_type
* int pending describes the client state
* none_pending, pending_waitee, pending_cont_waiter
* int nonstop_pending describes the nonstop state
* no_notifier_pending, pending_notifier, pending_notified
* int catch_syscalls;
* int syscall_op
* used to save and restore process->syscalls_to_catch for
situations where that is needed temporarily
* NO_SYSCALL, ANY_SYSCALL, XXX_SYSCALL
* Connecting another client
* Perhaps this piece could be a separate patch?
* event-loop.c
* wait_for_event :: now adds a select in case another client tries
to connect. add a new client if so.
* handle_file_event :: Set the client state if we are attaching to a
client already managed by gdbserver
* remote-utils.c
* get_remote_desc, get_listen_desc :: allow fds to be accessed
outside remote-utils.c
* handle_accept_event :: Set the default client_state to
the new fd and don't close the listen_desc
* write_prim, read_prim, readchar, putpkt, getpkt :: add a parameter
for fd
* clients_states mapping from file descriptor to client_state
* "indexed" by file descriptor of the client
* There is always a current client
* add_client_by_pid add another client state coordinating the same pid
* e.g. client 1 does 'run program' and client 2 is an strace that
attaches to the resulting pid
* additions for multiplexing clients
* clients have a state, an input, and are managed more or less as a FSM
* states: not-waiting waitee waiter step-waiter
* inputs: other, vContc, vConts, vContt, vStopped, vRun, vAttach, Hg,
g_or_m
* One client is always current and one is waiting.
* For a syscall the syscall client becomes the current client
* While the gdb client waits
* For a continue, next, or step the gdb client potentially waits if
a syscall is reached
* setup_multiplexing is invoked before the request case
in server.c::process serial event
* Check current state, packet input and other client's state
* If current client was doing continue or next, make it wait
* while the strace client handles the syscall
* e.g. current client 1 "not waiting" "vContc" and client 2 "not
waiting"
becomes client 1 "waiter" "vContc" and current client 2 "waitee"
* states:
packet type client1 state client2 state new c1 state new c2 state
vContc * none pending cont waiter waitee
vConts waitee none pending step waiter waitee
vContc waitee step waiter cont waiter waitee
vConts waitee step waiter step waiter waitee
vContc waitee cont waiter cont waiter *
vConts waitee cont waiter step waiter *
vContc * waitee cont waiter *
vConts * waitee step waiter *
* do_multiplexing is invoked after the request case
* complement of setup_multiplexing
* resolves the waiting client
* do_multiplexing handles most of the client switch situations
* A state machine that determines if a client's waiting should be
resolved
* do_multiplexing: Resolve the state of all the clients for a given
inferior after the packet handling switch
* Handles the case where the current client is continuing: has a
vContc or vConts packet
* Typically for an all stop client
* e.g. when a gdb client does a continue and a syscall is hit,
* resolve_waiter does the work of replying to a waiting client
* handle_status gets the status which is passed to the waiting client
* The non stop case just sets the client state to pending_notifier
* The remainder of the non stop work is done in notify_clients
* e.g. client 1 "waiter" "vContc"
* and current client 2 "waitee" "vContc"
* becomes client 1 "not waiting"
* and current client 2 "not waiting"
* if client 2 reached the breakpoint that client 1 was waiting for
* resolve_waiter wakes up a waiting client
* for all_stop mode: call handle_status and putpkt the result
* for non stop mode: create a vstop_notif and call notif_push
* process_event->handle_file_event->handle_target_event
->push_stop_notification->notif_push
* notify_clients notifies an attached client
* invoked by notif_push when it outputs a notification
* determines if the notification should be sent to another client
* notif_write_event->notify_clients
* example
stop queue at notify_clients: first
21487 status->kind = entered syscall
client state at notify_clients: first
*4 LWP 21487.21513 pc=0x7ffff78e39f7 vContc vContc waitee
syscall-entry
10 LWP 21487.21513 pc=0x7ffff78e39f7 vContc vContc waiter notifier
syscall-entry
notify_clients:791 after switch first=1 have_syscall=1 notifier=0/1
stop queue at notify_clients: after switch
21487 status->kind = entered syscall
client state at notify_clients: after switch
4 LWP 21487.21513 pc=0x7ffff78e39f7 vContc vContc waitee syscall-entry
*10 LWP 21487.21513 pc=0x7ffff78e39f7 vContc vContc waiter notifier
syscall-entry
notify_clients:801 Notifying fd=10 first and (same_is_waiter or
save_is_not_vstopped
putpkt/10 ("$OK#9a"); [noack mode]
putpkt/10 ("%Stop:T05syscall_entry:16;06:f06e7ff7ff7f0*
;07:b86e7ff7ff7f0* ;10:f7398ef7ff7f0*
;thread:p5409.53ef;thread:p5409.53ef;core:7;#27"); [notif]
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
multi-client.exp | 440
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
strace-threads.c | 76 +++++++++++++++++++
event-loop.c | 44 ++++-
linux-low.c | 3 +
notif.c | 4 +-
remote-utils.c | 97 ++++++----
remote-utils.h | 7 +-
server.c | 1113
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
server.h | 58 +++++-
diff --git a/event-loop.c b/event-loop.c
index a3310a05c8b..eddbbcdfa5f 100644
--- a/event-loop.c
+++ b/event-loop.c
@@ -19,6 +19,7 @@
/* Based on src/gdb/event-loop.c. */
#include "server.h"
+#include "remote-utils.h"
#include <sys/types.h>
#include "gdb_sys_time.h"
@@ -375,7 +376,11 @@ delete_file_handler (gdb_fildes_t fd)
;
prev_ptr->next_file = file_ptr->next_file;
}
- 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);
}
/* Handle the given event by calling the procedure associated to the
@@ -415,6 +420,12 @@ handle_file_event (gdb_fildes_t event_file_desc)
/* If there was a match, then call the handler. */
if (mask != 0)
{
+ /* 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,
file_ptr->client_data) < 0)
return -1;
@@ -455,6 +466,37 @@ wait_for_event (void)
file_handler *file_ptr;
int num_found = 0;
+ /* 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. */
fflush (stdout);
fflush (stderr);
diff --git a/linux-low.c b/linux-low.c
index 701f3e863c0..7f78b7a39fd 100644
--- a/linux-low.c
+++ b/linux-low.c
@@ -3084,6 +3084,9 @@ gdb_catch_this_syscall_p (struct lwp_info
*event_child)
if (proc->syscalls_to_catch.empty ())
return 0;
+ if (proc->syscalls_to_catch[0] == NO_SYSCALL)
+ return 0;
+
if (proc->syscalls_to_catch[0] == ANY_SYSCALL)
return 1;
diff --git a/notif.c b/notif.c
index 5ff7079123f..42ef793f255 100644
--- a/notif.c
+++ b/notif.c
@@ -67,6 +67,7 @@ notif_write_event (struct notif_server *notif, char
*own_buf)
= QUEUE_peek (notif_event_p, notif->queue);
notif->write (event, own_buf);
+ notify_clients (own_buf, 0);
}
else
write_ok (own_buf);
@@ -150,7 +151,8 @@ notif_push (struct notif_server *np, struct
notif_event *new_event)
p += strlen (p);
np->write (new_event, p);
- putpkt_notif (buf);
+ if (notify_clients (buf, 1))
+ putpkt_notif (buf);
}
}
diff --git a/remote-utils.c b/remote-utils.c
index 9199a9c7add..15a8b5cf337 100644
--- a/remote-utils.c
+++ b/remote-utils.c
@@ -94,7 +94,7 @@ enum {
Either NOT_SCHEDULED or the callback id. */
static int readchar_callback = NOT_SCHEDULED;
-static int readchar (void);
+static int readchar (gdb_fildes_t);
static void reset_readchar (void);
static void reschedule (void);
@@ -123,6 +123,25 @@ extern int debug_threads;
# define write(fd, buf, len) send (fd, (char *) buf, len, 0)
#endif
+
+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
gdb_connected (void)
{
@@ -151,7 +170,7 @@ enable_async_notification (int fd)
#endif
}
-static int
+int
handle_accept_event (int err, gdb_client_data client_data)
{
struct sockaddr_storage sockaddr;
@@ -164,6 +183,10 @@ handle_accept_event (int err, gdb_client_data
client_data)
if (remote_desc == -1)
perror_with_name ("Accept failed");
+ 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. */
socklen_t tmp = 1;
setsockopt (remote_desc, SOL_SOCKET, SO_KEEPALIVE,
@@ -180,7 +203,8 @@ handle_accept_event (int err, gdb_client_data
client_data)
exits when the remote side dies. */
#endif
- if (run_once)
+ /* Do not close in case another client tries to attach */
+ if (0 && run_once)
{
#ifndef USE_WIN32API
close (listen_desc); /* No longer need this */
@@ -191,7 +215,8 @@ handle_accept_event (int err, gdb_client_data
client_data)
/* Even if !RUN_ONCE no longer notice new connections. Still keep the
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); */
/* Convert IP address to string. */
char orig_host[GDB_NI_MAX_ADDR], orig_port[GDB_NI_MAX_PORT];
@@ -219,7 +244,9 @@ handle_accept_event (int err, gdb_client_data
client_data)
try to send vStopped notifications to GDB. But, don't do that
until GDB as selected all-stop/non-stop, and has queried the
threads' status ('?'). */
- target_async (0);
+
+ /* Do not async in case another client tries to attach */
+ /* target_async (0); */
return 0;
}
@@ -615,12 +642,12 @@ read_ptid (const char *buf, const char **obuf)
This may return less than COUNT. */
static int
-write_prim (const void *buf, int count)
+write_prim (gdb_fildes_t fd, const void *buf, int count)
{
if (remote_connection_is_stdio ())
return write (fileno (stdout), buf, count);
else
- return write (remote_desc, buf, count);
+ return write (fd, buf, count);
}
/* Read COUNT bytes from the client and store in BUF.
@@ -628,12 +655,12 @@ write_prim (const void *buf, int count)
This may return less than COUNT. */
static int
-read_prim (void *buf, int count)
+read_prim (gdb_fildes_t fd, void *buf, int count)
{
if (remote_connection_is_stdio ())
return read (fileno (stdin), buf, count);
else
- return read (remote_desc, buf, count);
+ return read (fd, buf, count);
}
/* Send a packet to the remote machine, with error checking.
@@ -674,7 +701,7 @@ putpkt_binary_1 (char *buf, int cnt, int is_notif)
do
{
- if (write_prim (buf2, p - buf2) != p - buf2)
+ if (write_prim (cs.file_desc, buf2, p - buf2) != p - buf2)
{
perror ("putpkt(write)");
free (buf2);
@@ -687,21 +714,21 @@ putpkt_binary_1 (char *buf, int cnt, int is_notif)
if (remote_debug)
{
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);
}
break;
}
if (remote_debug)
{
- 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);
if (cc < 0)
{
@@ -769,7 +796,7 @@ input_interrupt (int unused)
int cc;
char c = 0;
- cc = read_prim (&c, 1);
+ cc = read_prim (remote_desc, &c, 1);
if (cc == 0)
{
@@ -899,13 +926,13 @@ static unsigned char *readchar_bufp;
/* Returns next char from remote GDB. -1 if error. */
static int
-readchar (void)
+readchar (gdb_fildes_t fd)
{
int ch;
if (readchar_bufcnt == 0)
{
- readchar_bufcnt = read_prim (readchar_buf, sizeof (readchar_buf));
+ readchar_bufcnt = read_prim (fd, readchar_buf, sizeof
(readchar_buf));
if (readchar_bufcnt <= 0)
{
@@ -974,7 +1001,7 @@ reschedule (void)
and store it in BUF. Returns length of packet, or negative if
error. */
int
-getpkt (char *buf)
+getpkt (gdb_fildes_t fd, char *buf)
{
client_state &cs = get_client_state ();
char *bp;
@@ -987,7 +1014,7 @@ getpkt (char *buf)
while (1)
{
- c = readchar ();
+ c = readchar (fd);
/* The '\003' may appear before or after each packet, so
check for an input interrupt. */
@@ -1012,7 +1039,7 @@ getpkt (char *buf)
bp = buf;
while (1)
{
- c = readchar ();
+ c = readchar (fd);
if (c < 0)
return -1;
if (c == '#')
@@ -1022,8 +1049,8 @@ getpkt (char *buf)
}
*bp = 0;
- c1 = fromhex (readchar ());
- c2 = fromhex (readchar ());
+ c1 = fromhex (readchar (fd));
+ c2 = fromhex (readchar (fd));
if (csum == (c1 << 4) + c2)
break;
@@ -1040,7 +1067,7 @@ getpkt (char *buf)
fprintf (stderr, "Bad checksum, sentsum=0x%x, csum=0x%x, buf=%s\n",
(c1 << 4) + c2, csum, buf);
- if (write_prim ("-", 1) != 1)
+ if (write_prim (fd, "-", 1) != 1)
return -1;
}
@@ -1048,11 +1075,11 @@ getpkt (char *buf)
{
if (remote_debug)
{
- 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;
if (remote_debug)
@@ -1065,8 +1092,8 @@ getpkt (char *buf)
{
if (remote_debug)
{
- debug_printf ("getpkt (\"%s\"); [no ack sent] \n", buf);
- debug_flush ();
+ debug_printf ("getpkt/%d (\"%s\"); [no ack sent] \n", fd, buf);
+ fflush (stderr);
}
}
@@ -1082,7 +1109,7 @@ getpkt (char *buf)
while (readchar_bufcnt > 0 && *readchar_bufp == '\003')
{
/* Consume the interrupt character in the buffer. */
- readchar ();
+ readchar (fd);
(*the_target->request_interrupt) ();
}
@@ -1518,7 +1555,7 @@ look_up_one_symbol (const char *name, CORE_ADDR
*addrp, int may_ask_gdb)
return -1;
/* FIXME: Eventually add buffer overflow checking (to getpkt?) */
- len = getpkt (cs.own_buf);
+ len = getpkt (cs.file_desc, cs.own_buf);
if (len < 0)
return -1;
@@ -1556,7 +1593,7 @@ look_up_one_symbol (const char *name, CORE_ADDR
*addrp, int may_ask_gdb)
}
else
break;
- len = getpkt (cs.own_buf);
+ len = getpkt (cs.file_desc, cs.own_buf);
if (len < 0)
return -1;
}
@@ -1614,7 +1651,7 @@ relocate_instruction (CORE_ADDR *to, CORE_ADDR oldloc)
return -1;
/* FIXME: Eventually add buffer overflow checking (to getpkt?) */
- len = getpkt (cs.own_buf);
+ len = getpkt (cs.file_desc, cs.own_buf);
if (len < 0)
return -1;
@@ -1657,7 +1694,7 @@ relocate_instruction (CORE_ADDR *to, CORE_ADDR oldloc)
free (mem_buf);
if (putpkt (cs.own_buf) < 0)
return -1;
- len = getpkt (cs.own_buf);
+ len = getpkt (cs.file_desc, cs.own_buf);
if (len < 0)
return -1;
}
diff --git a/remote-utils.h b/remote-utils.h
index fcd3b3a93de..94a037f0597 100644
--- a/remote-utils.h
+++ b/remote-utils.h
@@ -19,6 +19,10 @@
#ifndef REMOTE_UTILS_H
#define REMOTE_UTILS_H
+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,7 +36,8 @@ char *write_ptid (char *buf, ptid_t ptid);
int putpkt (char *buf);
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);
void remote_open (const char *name);
void remote_close (void);
diff --git a/server.c b/server.c
index 4ec3548d644..14d15c0fb85 100644
--- a/server.c
+++ b/server.c
@@ -36,6 +36,8 @@
#include "dll.h"
#include "hostio.h"
#include <vector>
+#include <map>
+#include <string>
#include "common-inferior.h"
#include "job-control.h"
#include "environ.h"
@@ -135,6 +137,8 @@ int disable_packet_qfThreadInfo;
static unsigned char *mem_buf;
+static int attach_count;
+
/* A sub-class of 'struct notif_event' for stop, holding information
relative to a single stop reply. We keep a queue of these to
push to GDB in non-stop mode. */
@@ -156,18 +160,940 @@ static struct btrace_config current_btrace_conf;
DEFINE_QUEUE_P (notif_event_p);
-/* 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 */
-static client_state g_client_state;
+ 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;
+}
+
+/* Return the current client state. */
client_state &
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", NULL, 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 (¬if_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 (¤t_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 (¤t_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 (¤t_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 (¤t_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, ¤t_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;
+ }
+}
+
+
+/**********************************/
+
/* Put a stop reply to the stop reply queue. */
static void
@@ -210,14 +1136,6 @@ discard_queued_stop_replies (ptid_t ptid)
remove_all_on_match_ptid, &ptid);
}
-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);
-}
-
/* QUEUE_iterate callback helper for in_queued_stop_replies. */
static int
@@ -250,11 +1168,6 @@ in_queued_stop_replies (ptid_t ptid)
in_queued_stop_replies_ptid, &ptid);
}
-struct notif_server notif_stop =
-{
- "vStopped", "Stop", NULL, vstop_notif_reply,
-};
-
static int
target_running (void)
{
@@ -324,8 +1237,6 @@ attach_inferior (int pid)
return 0;
}
-extern int remote_debug;
-
/* Decode a qXfer read request. Return 0 if everything looks OK,
or -1 otherwise. */
@@ -645,6 +1556,7 @@ handle_general_set (char *own_buf)
}
else
process->syscalls_to_catch.push_back (ANY_SYSCALL);
+ client_states.current_cs->catch_syscalls = 1;
}
write_ok (own_buf);
@@ -745,6 +1657,13 @@ handle_general_set (char *own_buf)
return;
}
+ if (client_states.cs.size() > 2 && (non_stop ^ req))
+ {
+ write_enn (own_buf);
+ return;
+ }
+
+
req_str = req ? "non-stop" : "all-stop";
if (start_non_stop (req) != 0)
{
@@ -960,6 +1879,8 @@ monitor_show_help (void)
monitor_output (" Options: all, none");
monitor_output (", timestamp");
monitor_output ("\n");
+ monitor_output (" client status\n");
+ monitor_output (" Display status of multiple clients\n");
monitor_output (" exit\n");
monitor_output (" Quit GDBserver\n");
}
@@ -1198,6 +2119,13 @@ handle_detach (char *own_buf)
process_info *process;
+ 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)
{
/* skip 'D;' */
@@ -1398,6 +2326,43 @@ handle_monitor_command (char *mon, char *own_buf)
write_enn (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, "help") == 0)
monitor_show_help ();
else if (strcmp (mon, "exit") == 0)
@@ -1653,6 +2618,7 @@ handle_qxfer_statictrace (const char *annex,
static void
handle_qxfer_threads_worker (thread_info *thread, struct buffer *buffer)
{
+ client_state &cs = get_client_state ();
ptid_t ptid = ptid_of (thread);
char ptid_s[100];
int core = target_core_of_thread (ptid);
@@ -1662,6 +2628,11 @@ handle_qxfer_threads_worker (thread_info *thread,
struct buffer *buffer)
gdb_byte *handle;
bool handle_status = target_thread_handle (ptid, &handle, &handle_len);
+ /* 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);
buffer_xml_printf (buffer, "<thread id=\"%s\"", ptid_s);
@@ -2635,7 +3606,7 @@ handle_query (char *own_buf, int packet_len, int
*new_packet_len_p)
return;
}
- strcpy (own_buf, process->attached ? "1" : "0");
+ strcpy (own_buf, process->attached || have_multiple_clients
(cs.file_desc) ? "1" : "0");
return;
}
@@ -2910,9 +3881,27 @@ handle_v_attach (char *own_buf)
{
client_state &cs = get_client_state ();
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)
{
/* Don't report shared library events after attaching, even if
some libraries are preloaded. GDB will always poll the
@@ -3336,8 +4325,8 @@ handle_status (char *own_buf)
{
for_each_thread (queue_stop_reply_callback);
- /* 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). */
notif_write_event (¬if_stop, cs.own_buf);
}
@@ -3572,6 +4561,9 @@ captured_main (int argc, char *argv[])
volatile int multi_mode = 0;
volatile int attach = 0;
int was_running;
+
+ int remote_desc = get_remote_desc();
+ client_states.set_client_state (remote_desc);
bool selftest = false;
#if GDB_SELF_TEST
const char *selftest_filter = NULL;
@@ -4036,16 +5028,33 @@ process_serial_event (void)
disable_async_io ();
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;
char ch = cs.own_buf[0];
+
+ cs.packet_type = get_packet_type (cs);
+
+ if (! setup_multiplexing (cs))
+ return 0;
+
switch (ch)
{
case 'q':
@@ -4368,6 +5377,9 @@ process_serial_event (void)
break;
}
+ if (! do_multiplexing (get_client_state()))
+ return 0;
+
if (new_packet_len != -1)
putpkt_binary (cs.own_buf, new_packet_len);
else
@@ -4391,6 +5403,7 @@ handle_serial_event (int err, gdb_client_data
client_data)
/* Really handle it. */
if (process_serial_event () < 0)
+
return -1;
/* Be sure to not change the selected thread behind GDB's back.
@@ -4400,6 +5413,29 @@ handle_serial_event (int err, gdb_client_data
client_data)
return 0;
}
+/* Dump stop notifications */
+
+static void
+dump_stop_queue (const char *function, const char *comment)
+{
+ struct queue_elem_notif_event_p *queue_elem;
+ struct vstop_notif *vstop_notif;
+
+ if (!debug_threads)
+ return;
+ if (notif_stop.queue->head == 0)
+ return;
+ debug_printf ("** stop queue at %s: %s\n", function, comment);
+ for (queue_elem = notif_stop.queue->head;
+ queue_elem != 0;
+ queue_elem = queue_elem->next)
+ {
+ vstop_notif = (struct vstop_notif*)queue_elem->data;
+ debug_printf ("%d %s\n", vstop_notif->ptid.pid(),
target_waitstatus_to_string (&vstop_notif->status).c_str());
+ }
+}
+
+
/* Push a stop notification on the notification queue. */
static void
@@ -4418,13 +5454,20 @@ push_stop_notification (ptid_t ptid, struct
target_waitstatus *status)
int
handle_target_event (int err, gdb_client_data client_data)
{
+ client_state *csi, *save_csi;
client_state &cs = get_client_state ();
+
if (debug_threads)
debug_printf ("handling possible target event\n");
cs.last_ptid = mywait (minus_one_ptid, &cs.last_status,
TARGET_WNOHANG, 1);
+ if (debug_threads)
+ debug_printf ("%s fd=%d queue=%d %s\n", __FUNCTION__,
client_states.current_cs->file_desc, notif_stop.queue->head == NULL ? 0 : 1,
+ waitkind_str[cs.last_status.kind]);
+ save_csi = client_states.current_cs;
+
if (cs.last_status.kind == TARGET_WAITKIND_NO_RESUMED)
{
if (gdb_connected () && report_no_resumed)
@@ -4436,6 +5479,25 @@ handle_target_event (int err, gdb_client_data
client_data)
struct process_info *process = find_process_pid (pid);
int forward_event = !gdb_connected () || process->gdb_detached;
+ 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
|| cs.last_status.kind == TARGET_WAITKIND_SIGNALLED)
{
@@ -4492,6 +5554,7 @@ handle_target_event (int err, gdb_client_data
client_data)
/* Be sure to not change the selected thread behind GDB's back.
Important in the non-stop mode asynchronous protocol. */
set_desired_thread ();
+ client_states.set_client_state (save_csi->file_desc);
return 0;
}
diff --git a/server.h b/server.h
index 5e41e2f595d..f423406aa22 100644
--- a/server.h
+++ b/server.h
@@ -61,6 +61,8 @@ int vsnprintf(char *str, size_t size, const char
*format, va_list ap);
#include "target.h"
#include "mem-break.h"
#include "environ.h"
+#include <list>
+#include <map>
/* Target-specific functions */
@@ -92,6 +94,7 @@ extern void handle_v_requests (char *own_buf, int
packet_len,
int *new_packet_len);
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);
/* Get rid of the currently pending stop replies that match PTID. */
extern void discard_queued_stop_replies (ptid_t ptid);
@@ -121,6 +124,9 @@ extern int in_queued_stop_replies (ptid_t ptid);
/* Definition for any syscall, used for unfiltered syscall reporting. */
#define ANY_SYSCALL (-2)
+/* Trace child processes created via fork or clone. */
+#define NO_SYSCALL (-3)
+
/* After fork_inferior has been called, we need to adjust a few
signals and call startup_inferior to start the inferior and consume
its first events. This is done here. PID is the pid of the new
@@ -133,6 +139,17 @@ extern gdb_environ *get_environ ();
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
connected client. */
@@ -141,6 +158,19 @@ struct client_state
client_state ():
own_buf ((char *) xmalloc (PBUFSIZ + 1))
{}
+ 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;
/* The thread set with an `Hc' packet. `Hc' is deprecated in favor of
`vCont'. Note the multi-process extensions made `vCont' a
@@ -148,10 +178,10 @@ struct client_state
CONT_THREAD can be null_ptid for no `Hc' thread, minus_one_ptid for
resuming all threads of the process (again, `Hc' isn't used for
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; // +
int multi_process = 0;
int report_fork_events = 0;
@@ -183,8 +213,8 @@ struct client_state
int program_signals_p = 0;
/* Last status reported to GDB. */
- struct target_waitstatus last_status;
- ptid_t last_ptid;
+ struct target_waitstatus last_status; // +
+ ptid_t last_ptid; // +
char *own_buf;
@@ -198,9 +228,27 @@ struct client_state
int current_traceframe = -1;
+ 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);
#include "gdbthread.h"
#include "inferiors.h"
diff --git a/multi-client.exp b/multi-client.exp
new file mode 100644
index 00000000000..9b06b012824
--- /dev/null
+++ b/multi-client.exp
@@ -0,0 +1,440 @@
+# Copyright (C) 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
+}
diff --git a/strace-threads.c b/strace-threads.c
new file mode 100644
index 00000000000..050377e1b4e
--- /dev/null
+++ b/strace-threads.c
@@ -0,0 +1,76 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/syscall.h>
+#include <errno.h>
+
+static pthread_barrier_t barrier;
+
+void *thread_worker ();
+
+
+int
+main(int argc, char *argv[])
+{
+ pthread_t *thread;
+ sigset_t mask1;
+ int thread_count;
+
+ if (argc == 1)
+ thread_count = 1;
+ else
+ {
+ thread_count = strtol(argv[1], NULL, 10);
+ if (errno != 0)
+ {
+ perror("strtol");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ thread = alloca (sizeof (pthread_t) * (thread_count + 1));
+ sigemptyset (&mask1);
+ sigaddset (&mask1, SIGUSR1);
+ sigprocmask (SIG_UNBLOCK, &mask1, NULL);
+
+ pthread_barrier_init (&barrier, NULL, thread_count);
+
+ for (int i = 1; i <= thread_count; i++)
+ pthread_create (&thread[i], NULL, & thread_worker, NULL);
+
+ for (int i = 1; i <= thread_count; i++)
+ pthread_join (thread[i], NULL);
+
+ exit (EXIT_SUCCESS);
+}
+
+void*
+thread_worker ()
+{
+ pthread_barrier_wait (&barrier);
+
+ int unknown_syscall = 123456789;
+ int fd[2];
+ char buf1[2] = "a";
+ char buf2[2];
+
+ close (-1);
+
+ chroot (".");
+
+ pipe (fd);
+
+ write (fd[1], buf1, sizeof (buf1));
+
+ read (fd[0], buf2, sizeof (buf2));
+
+ /* Test vfork-event interactions. Child exits immediately.
+ (Plain fork won't work on no-mmu kernel configurations.) */
+ // if (vfork () == 0)
+ // _exit (0);
+
+ syscall (unknown_syscall);
+}
+