@@ -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);
@@ -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;
@@ -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);
}
}
@@ -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;
}
@@ -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);
@@ -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;
}
@@ -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"
new file mode 100644
@@ -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
+}
new file mode 100644
@@ -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);
+}
+