/* Main code for remote server for GDB.
- Copyright (C) 1989, 1993-1995, 1997-2000, 2002-2012 Free Software
- Foundation, Inc.
+ Copyright (C) 1989-2013 Free Software Foundation, Inc.
This file is part of GDB.
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "server.h"
+#include "gdbthread.h"
+#include "agent.h"
+#include "notif.h"
#if HAVE_UNISTD_H
#include <unistd.h>
#if HAVE_SIGNAL_H
#include <signal.h>
#endif
-#if HAVE_SYS_WAIT_H
-#include <sys/wait.h>
-#endif
-
+#include "gdb_wait.h"
+
+/* The thread set with an `Hc' packet. `Hc' is deprecated in favor of
+ `vCont'. Note the multi-process extensions made `vCont' a
+ requirement, so `Hc pPID.TID' is pretty much undefined. So
+ 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.
+
+ We also set this when handling a single-thread `vCont' resume, as
+ some places in the backends check it to know when (and for which
+ thread) single-thread scheduler-locking is in effect. */
ptid_t cont_thread;
+
+/* The thread set with an `Hg' packet. */
ptid_t general_thread;
int server_waiting;
/* Enable debugging of h/w breakpoint/watchpoint support. */
int debug_hw_points;
-int pass_signals[TARGET_SIGNAL_LAST];
+int pass_signals[GDB_SIGNAL_LAST];
+int program_signals[GDB_SIGNAL_LAST];
+int program_signals_p;
jmp_buf toplevel;
static char *own_buf;
static unsigned char *mem_buf;
-/* Structure holding information relative to a single stop reply. We
- keep a queue of these (really a singly-linked list) to push to GDB
- in non-stop mode. */
+/* 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. */
+
struct vstop_notif
{
- /* Pointer to next in list. */
- struct vstop_notif *next;
+ struct notif_event base;
/* Thread or process that got the event. */
ptid_t ptid;
struct target_waitstatus status;
};
-/* The pending stop replies list head. */
-static struct vstop_notif *notif_queue = NULL;
+DEFINE_QUEUE_P (notif_event_p);
/* Put a stop reply to the stop reply queue. */
static void
queue_stop_reply (ptid_t ptid, struct target_waitstatus *status)
{
- struct vstop_notif *new_notif;
+ struct vstop_notif *new_notif = xmalloc (sizeof (*new_notif));
- new_notif = xmalloc (sizeof (*new_notif));
- new_notif->next = NULL;
new_notif->ptid = ptid;
new_notif->status = *status;
- if (notif_queue)
- {
- struct vstop_notif *tail;
- for (tail = notif_queue;
- tail && tail->next;
- tail = tail->next)
- ;
- tail->next = new_notif;
- }
- else
- notif_queue = new_notif;
-
- if (remote_debug)
- {
- int i = 0;
- struct vstop_notif *n;
-
- for (n = notif_queue; n; n = n->next)
- i++;
-
- fprintf (stderr, "pending stop replies: %d\n", i);
- }
+ notif_event_enque (¬if_stop, (struct notif_event *) new_notif);
}
-/* Place an event in the stop reply queue, and push a notification if
- we aren't sending one yet. */
-
-void
-push_event (ptid_t ptid, struct target_waitstatus *status)
+static int
+remove_all_on_match_pid (QUEUE (notif_event_p) *q,
+ QUEUE_ITER (notif_event_p) *iter,
+ struct notif_event *event,
+ void *data)
{
- gdb_assert (status->kind != TARGET_WAITKIND_IGNORE);
-
- queue_stop_reply (ptid, status);
+ int *pid = data;
- /* If this is the first stop reply in the queue, then inform GDB
- about it, by sending a Stop notification. */
- if (notif_queue->next == NULL)
+ if (*pid == -1
+ || ptid_get_pid (((struct vstop_notif *) event)->ptid) == *pid)
{
- char *p = own_buf;
- strcpy (p, "Stop:");
- p += strlen (p);
- prepare_resume_reply (p,
- notif_queue->ptid, ¬if_queue->status);
- putpkt_notif (own_buf);
+ if (q->free_func != NULL)
+ q->free_func (event);
+
+ QUEUE_remove_elem (notif_event_p, q, iter);
}
+
+ return 1;
}
/* Get rid of the currently pending stop replies for PID. If PID is
static void
discard_queued_stop_replies (int pid)
{
- struct vstop_notif *prev = NULL, *reply, *next;
-
- for (reply = notif_queue; reply; reply = next)
- {
- next = reply->next;
-
- if (pid == -1
- || ptid_get_pid (reply->ptid) == pid)
- {
- if (reply == notif_queue)
- notif_queue = next;
- else
- prev->next = reply->next;
-
- free (reply);
- }
- else
- prev = reply;
- }
+ QUEUE_iterate (notif_event_p, notif_stop.queue,
+ remove_all_on_match_pid, &pid);
}
-/* If there are more stop replies to push, push one now. */
-
static void
-send_next_stop_reply (char *own_buf)
+vstop_notif_reply (struct notif_event *event, char *own_buf)
{
- if (notif_queue)
- prepare_resume_reply (own_buf,
- notif_queue->ptid,
- ¬if_queue->status);
- else
- write_ok (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,
+};
+
static int
target_running (void)
{
signal (SIGTTIN, SIG_DFL);
#endif
+ /* Clear this so the backend doesn't get confused, thinking
+ CONT_THREAD died, and it needs to resume all threads. */
+ cont_thread = null_ptid;
+
signal_pid = create_inferior (new_argv[0], new_argv);
/* FIXME: we don't actually know at this point that the create
current_inferior->last_resume_kind = resume_stop;
current_inferior->last_status = last_status;
}
- while (last_status.value.sig != TARGET_SIGNAL_TRAP);
+ while (last_status.value.sig != GDB_SIGNAL_TRAP);
- current_inferior->last_resume_kind = resume_stop;
- current_inferior->last_status = last_status;
return signal_pid;
}
process using the "attach" command, but this is different; it's
just using "target remote". Pretend it's just starting up. */
if (last_status.kind == TARGET_WAITKIND_STOPPED
- && last_status.value.sig == TARGET_SIGNAL_STOP)
- last_status.value.sig = TARGET_SIGNAL_TRAP;
+ && last_status.value.sig == GDB_SIGNAL_STOP)
+ last_status.value.sig = GDB_SIGNAL_TRAP;
current_inferior->last_resume_kind = resume_stop;
current_inferior->last_status = last_status;
{
if (strncmp ("QPassSignals:", own_buf, strlen ("QPassSignals:")) == 0)
{
- int numsigs = (int) TARGET_SIGNAL_LAST, i;
+ int numsigs = (int) GDB_SIGNAL_LAST, i;
const char *p = own_buf + strlen ("QPassSignals:");
CORE_ADDR cursig;
return;
}
+ if (strncmp ("QProgramSignals:", own_buf, strlen ("QProgramSignals:")) == 0)
+ {
+ int numsigs = (int) GDB_SIGNAL_LAST, i;
+ const char *p = own_buf + strlen ("QProgramSignals:");
+ CORE_ADDR cursig;
+
+ program_signals_p = 1;
+
+ p = decode_address_to_semicolon (&cursig, p);
+ for (i = 0; i < numsigs; i++)
+ {
+ if (i == cursig)
+ {
+ program_signals[i] = 1;
+ if (*p == '\0')
+ /* Keep looping, to clear the remaining signals. */
+ cursig = -1;
+ else
+ p = decode_address_to_semicolon (&cursig, p);
+ }
+ else
+ program_signals[i] = 0;
+ }
+ strcpy (own_buf, "OK");
+ return;
+ }
+
if (strcmp (own_buf, "QStartNoAckMode") == 0)
{
if (remote_debug)
&& handle_tracepoint_general_set (own_buf))
return;
+ if (strncmp ("QAgent:", own_buf, strlen ("QAgent:")) == 0)
+ {
+ char *mode = own_buf + strlen ("QAgent:");
+ int req = 0;
+
+ if (strcmp (mode, "0") == 0)
+ req = 0;
+ else if (strcmp (mode, "1") == 0)
+ req = 1;
+ else
+ {
+ /* We don't know what this value is, so complain to GDB. */
+ sprintf (own_buf, "E.Unknown QAgent value");
+ return;
+ }
+
+ /* Update the flag. */
+ use_agent = req;
+ if (remote_debug)
+ fprintf (stderr, "[%s agent]\n", req ? "Enable" : "Disable");
+ write_ok (own_buf);
+ return;
+ }
+
/* Otherwise we didn't know what packet it was. Say we didn't
understand it. */
own_buf[0] = 0;
if (gdb_read_memory (start_addr, search_buf, search_buf_size)
!= search_buf_size)
{
- warning ("Unable to access target memory at 0x%lx, halting search.",
- (long) start_addr);
+ warning ("Unable to access %ld bytes of target "
+ "memory at 0x%lx, halting search.",
+ (long) search_buf_size, (long) start_addr);
return -1;
}
if (gdb_read_memory (read_addr, search_buf + keep_len,
nr_to_read) != search_buf_size)
{
- warning ("Unable to access target memory "
+ warning ("Unable to access %ld bytes of target memory "
"at 0x%lx, halting search.",
- (long) read_addr);
+ (long) nr_to_read, (long) read_addr);
return -1;
}
if (annex[0] != '\0' || !target_running ())
return -1;
- /* Do not confuse this packet with qXfer:libraries-svr4:read. */
- if (the_target->qxfer_libraries_svr4 != NULL)
- return 0;
-
/* Over-estimate the necessary memory. Assume that every character
in the library name must be escaped. */
total_len = 64;
{
ptid_t ptid = thread_to_gdb_id ((struct thread_info *)thread);
char ptid_s[100];
- int core = -1;
+ int core = target_core_of_thread (ptid);
char core_s[21];
write_ptid (ptid_s, ptid);
- if (the_target->core_of_thread)
- core = (*the_target->core_of_thread) (ptid);
-
if (core != -1)
{
sprintf (core_s, "%d", core);
free (qsupported);
}
- sprintf (own_buf, "PacketSize=%x;QPassSignals+", PBUFSIZ - 1);
+ sprintf (own_buf,
+ "PacketSize=%x;QPassSignals+;QProgramSignals+",
+ PBUFSIZ - 1);
if (the_target->qxfer_libraries_svr4 != NULL)
strcat (own_buf, ";qXfer:libraries-svr4:read+");
strcat (own_buf, ";qXfer:statictrace:read+");
strcat (own_buf, ";qXfer:traceframe-info:read+");
strcat (own_buf, ";EnableDisableTracepoints+");
+ strcat (own_buf, ";QTBuffer:size+");
strcat (own_buf, ";tracenz+");
}
+ /* Support target-side breakpoint conditions and commands. */
+ strcat (own_buf, ";ConditionalBreakpoints+");
+ strcat (own_buf, ";BreakpointCommands+");
+
+ if (target_supports_agent ())
+ strcat (own_buf, ";QAgent+");
+
return;
}
{
/* CRC check (compare-section). */
char *comma;
- CORE_ADDR base;
+ ULONGEST base;
int len;
unsigned long long crc;
require_running (own_buf);
- base = strtoul (own_buf + 5, &comma, 16);
+ comma = unpack_varlen_hex (own_buf + 5, &base);
if (*comma++ != ',')
{
write_enn (own_buf);
goto err;
p = q;
- if (!target_signal_to_host_p (sig))
+ if (!gdb_signal_to_host_p (sig))
goto err;
- resume_info[i].sig = target_signal_to_host (sig);
+ resume_info[i].sig = gdb_signal_to_host (sig);
}
else
{
if (i < n)
resume_info[i] = default_action;
- /* Still used in occasional places in the backend. */
+ /* `cont_thread' is still used in occasional places in the backend,
+ to implement single-thread scheduler-locking. Doesn't make sense
+ to set it if we see a stop request, or a wildcard action (one
+ with '-1' (all threads), or 'pPID.-1' (all threads of PID)). */
if (n == 1
- && !ptid_equal (resume_info[0].thread, minus_one_ptid)
+ && !(ptid_equal (resume_info[0].thread, minus_one_ptid)
+ || ptid_get_lwp (resume_info[0].thread) == -1)
&& resume_info[0].kind != resume_stop)
cont_thread = resume_info[0].thread;
else
if (pid != 0 && kill_inferior (pid) == 0)
{
last_status.kind = TARGET_WAITKIND_SIGNALLED;
- last_status.value.sig = TARGET_SIGNAL_KILL;
+ last_status.value.sig = GDB_SIGNAL_KILL;
last_ptid = pid_to_ptid (pid);
discard_queued_stop_replies (pid);
write_ok (own_buf);
}
}
-/* Handle a 'vStopped' packet. */
-static void
-handle_v_stopped (char *own_buf)
-{
- /* If we're waiting for GDB to acknowledge a pending stop reply,
- consider that done. */
- if (notif_queue)
- {
- struct vstop_notif *head;
-
- if (remote_debug)
- fprintf (stderr, "vStopped: acking %s\n",
- target_pid_to_str (notif_queue->ptid));
-
- head = notif_queue;
- notif_queue = notif_queue->next;
- free (head);
- }
-
- /* Push another stop reply, or if there are no more left, an OK. */
- send_next_stop_reply (own_buf);
-}
-
/* Handle all of the extended 'v' packets. */
void
handle_v_requests (char *own_buf, int packet_len, int *new_packet_len)
return;
}
- if (strncmp (own_buf, "vStopped", 8) == 0)
- {
- handle_v_stopped (own_buf);
- return;
- }
+ if (handle_notif_ack (own_buf, packet_len))
+ return;
/* Otherwise we didn't know what packet it was. Say we didn't
understand it. */
if (step || sig || valid_cont_thread)
{
- resume_info[0].thread
- = ((struct inferior_list_entry *) current_inferior)->id;
+ resume_info[0].thread = current_ptid;
if (step)
resume_info[0].kind = resume_step;
else
manage the thread's last_status field. */
if (the_target->thread_stopped == NULL)
{
+ struct vstop_notif *new_notif = xmalloc (sizeof (*new_notif));
+
+ new_notif->ptid = entry->id;
+ new_notif->status = thread->last_status;
/* Pass the last stop reply back to GDB, but don't notify
yet. */
- queue_stop_reply (entry->id, &thread->last_status);
+ notif_event_enque (¬if_stop,
+ (struct notif_event *) new_notif);
}
else
{
/* Most threads are stopped implicitly (all-stop); tag that with
signal 0. */
thread->last_status.kind = TARGET_WAITKIND_STOPPED;
- thread->last_status.value.sig = TARGET_SIGNAL_0;
+ thread->last_status.value.sig = GDB_SIGNAL_0;
}
}
/* The first is sent immediatly. OK is sent if there is no
stopped thread, which is the same handling of the vStopped
packet (by design). */
- send_next_stop_reply (own_buf);
+ notif_write_event (¬if_stop, own_buf);
}
else
{
struct target_waitstatus status;
status.kind = TARGET_WAITKIND_STOPPED;
- status.value.sig = TARGET_SIGNAL_TRAP;
+ status.value.sig = GDB_SIGNAL_TRAP;
prepare_resume_reply (own_buf,
all_threads.head->id, &status);
}
gdbserver_version (void)
{
printf ("GNU gdbserver %s%s\n"
- "Copyright (C) 2012 Free Software Foundation, Inc.\n"
+ "Copyright (C) 2013 Free Software Foundation, Inc.\n"
"gdbserver is free software, covered by the "
"GNU General Public License.\n"
"This gdbserver was configured as \"%s\"\n",
int pid;
char *arg_end, *port;
char **next_arg = &argv[1];
- int multi_mode = 0;
- int attach = 0;
+ volatile int multi_mode = 0;
+ volatile int attach = 0;
int was_running;
while (*next_arg != NULL && **next_arg == '-')
initialize_async_io ();
initialize_low ();
+ initialize_event_loop ();
if (target_supports_tracepoints ())
initialize_tracepoint ();
last_ptid = minus_one_ptid;
}
+ initialize_notif ();
+
/* Don't report shared library events on the initial connection,
even if some libraries are preloaded. Avoids the "stopped by
shared library event" notice on gdb side. */
}
}
+/* Process options coming from Z packets for *point at address
+ POINT_ADDR. PACKET is the packet buffer. *PACKET is updated
+ to point to the first char after the last processed option. */
+
+static void
+process_point_options (CORE_ADDR point_addr, char **packet)
+{
+ char *dataptr = *packet;
+ int persist;
+
+ /* Check if data has the correct format. */
+ if (*dataptr != ';')
+ return;
+
+ dataptr++;
+
+ while (*dataptr)
+ {
+ if (*dataptr == ';')
+ ++dataptr;
+
+ if (*dataptr == 'X')
+ {
+ /* Conditional expression. */
+ if (debug_threads)
+ fprintf (stderr, "Found breakpoint condition.\n");
+ add_breakpoint_condition (point_addr, &dataptr);
+ }
+ else if (strncmp (dataptr, "cmds:", strlen ("cmds:")) == 0)
+ {
+ dataptr += strlen ("cmds:");
+ if (debug_threads)
+ fprintf (stderr, "Found breakpoint commands %s.\n", dataptr);
+ persist = (*dataptr == '1');
+ dataptr += 2;
+ add_breakpoint_commands (point_addr, &dataptr, persist);
+ }
+ else
+ {
+ fprintf (stderr, "Unknown token %c, ignoring.\n",
+ *dataptr);
+ /* Skip tokens until we find one that we recognize. */
+ while (*dataptr && *dataptr != ';')
+ dataptr++;
+ }
+ }
+ *packet = dataptr;
+}
+
/* Event loop callback that handles a serial event. The first byte in
the serial buffer gets us here. We expect characters to arrive at
a brisk pace, so we read the rest of the packet with a blocking
pid = strtol (&own_buf[i], NULL, 16);
}
else
- pid =
- ptid_get_pid (((struct inferior_list_entry *) current_inferior)->id);
+ pid = ptid_get_pid (current_ptid);
- if (tracing && disconnected_tracing)
+ if ((tracing && disconnected_tracing) || any_persistent_commands ())
{
struct thread_resume resume_info;
struct process_info *process = find_process_pid (pid);
break;
}
- fprintf (stderr,
- "Disconnected tracing in effect, "
- "leaving gdbserver attached to the process\n");
+ if (tracing && disconnected_tracing)
+ fprintf (stderr,
+ "Disconnected tracing in effect, "
+ "leaving gdbserver attached to the process\n");
+
+ if (any_persistent_commands ())
+ fprintf (stderr,
+ "Persistent commands are present, "
+ "leaving gdbserver attached to the process\n");
/* Make sure we're in non-stop/async mode, so we we can both
wait for an async socket accept, and handle async target
case 'C':
require_running (own_buf);
convert_ascii_to_int (own_buf + 1, &sig, 1);
- if (target_signal_to_host_p (sig))
- signal = target_signal_to_host (sig);
+ if (gdb_signal_to_host_p (sig))
+ signal = gdb_signal_to_host (sig);
else
signal = 0;
myresume (own_buf, 0, signal);
case 'S':
require_running (own_buf);
convert_ascii_to_int (own_buf + 1, &sig, 1);
- if (target_signal_to_host_p (sig))
- signal = target_signal_to_host (sig);
+ if (gdb_signal_to_host_p (sig))
+ signal = gdb_signal_to_host (sig);
else
signal = 0;
myresume (own_buf, 1, signal);
/* Fallthrough. */
case 'z': /* remove_ ... */
{
- char *lenptr;
char *dataptr;
- CORE_ADDR addr = strtoul (&own_buf[3], &lenptr, 16);
- int len = strtol (lenptr + 1, &dataptr, 16);
+ ULONGEST addr;
+ int len;
char type = own_buf[1];
int res;
const int insert = ch == 'Z';
+ char *p = &own_buf[3];
+
+ p = unpack_varlen_hex (p, &addr);
+ len = strtol (p + 1, &dataptr, 16);
/* Default to unrecognized/unsupported. */
res = 1;
case '4': /* access watchpoint */
require_running (own_buf);
if (insert && the_target->insert_point != NULL)
- res = (*the_target->insert_point) (type, addr, len);
+ {
+ /* Insert the breakpoint. If it is already inserted, nothing
+ will take place. */
+ res = (*the_target->insert_point) (type, addr, len);
+
+ /* GDB may have sent us a list of *point parameters to be
+ evaluated on the target's side. Read such list here. If we
+ already have a list of parameters, GDB is telling us to drop
+ that list and use this one instead. */
+ if (!res && (type == '0' || type == '1'))
+ {
+ /* Remove previous conditions. */
+ clear_gdb_breakpoint_conditions (addr);
+ process_point_options (addr, &dataptr);
+ }
+ }
else if (!insert && the_target->remove_point != NULL)
res = (*the_target->remove_point) (type, addr, len);
break;
if (extended_protocol)
{
last_status.kind = TARGET_WAITKIND_EXITED;
- last_status.value.sig = TARGET_SIGNAL_KILL;
+ last_status.value.sig = GDB_SIGNAL_KILL;
return 0;
}
else
else
{
last_status.kind = TARGET_WAITKIND_EXITED;
- last_status.value.sig = TARGET_SIGNAL_KILL;
+ last_status.value.sig = GDB_SIGNAL_KILL;
}
return 0;
}
{
/* In non-stop, defer exiting until GDB had a chance to query
the whole vStopped list (until it gets an OK). */
- if (!notif_queue)
+ if (QUEUE_is_empty (notif_event_p, notif_stop.queue))
{
fprintf (stderr, "GDBserver exiting\n");
remote_close ();
resume_info.thread = last_ptid;
resume_info.kind = resume_continue;
- resume_info.sig = target_signal_to_host (last_status.value.sig);
+ resume_info.sig = gdb_signal_to_host (last_status.value.sig);
(*the_target->resume) (&resume_info, 1);
}
else if (debug_threads)
}
else
{
- /* Something interesting. Tell GDB about it. */
- push_event (last_ptid, &last_status);
+ struct vstop_notif *vstop_notif
+ = xmalloc (sizeof (struct vstop_notif));
+
+ vstop_notif->status = last_status;
+ vstop_notif->ptid = last_ptid;
+ /* Push Stop notification. */
+ notif_push (¬if_stop,
+ (struct notif_event *) vstop_notif);
}
}