/* 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.
#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"
+#include "btrace-common.h"
/* The thread set with an `Hc' packet. `Hc' is deprecated in favor of
`vCont'. Note the multi-process extensions made `vCont' a
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);
+ int *pid = data;
- queue_stop_reply (ptid, status);
-
- /* 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)
{
PBUFSIZ - 2) + 1;
}
+/* Handle btrace enabling. */
+
+static const char *
+handle_btrace_enable (struct thread_info *thread)
+{
+ if (thread->btrace != NULL)
+ return "E.Btrace already enabled.";
+
+ thread->btrace = target_enable_btrace (thread->entry.id);
+ if (thread->btrace == NULL)
+ return "E.Could not enable btrace.";
+
+ return NULL;
+}
+
+/* Handle btrace disabling. */
+
+static const char *
+handle_btrace_disable (struct thread_info *thread)
+{
+
+ if (thread->btrace == NULL)
+ return "E.Branch tracing not enabled.";
+
+ if (target_disable_btrace (thread->btrace) != 0)
+ return "E.Could not disable branch tracing.";
+
+ thread->btrace = NULL;
+ return NULL;
+}
+
+/* Handle the "Qbtrace" packet. */
+
+static int
+handle_btrace_general_set (char *own_buf)
+{
+ struct thread_info *thread;
+ const char *err;
+ char *op;
+
+ if (strncmp ("Qbtrace:", own_buf, strlen ("Qbtrace:")) != 0)
+ return 0;
+
+ op = own_buf + strlen ("Qbtrace:");
+
+ if (!target_supports_btrace ())
+ {
+ strcpy (own_buf, "E.Target does not support branch tracing.");
+ return -1;
+ }
+
+ if (ptid_equal (general_thread, null_ptid)
+ || ptid_equal (general_thread, minus_one_ptid))
+ {
+ strcpy (own_buf, "E.Must select a single thread.");
+ return -1;
+ }
+
+ thread = find_thread_ptid (general_thread);
+ if (thread == NULL)
+ {
+ strcpy (own_buf, "E.No such thread.");
+ return -1;
+ }
+
+ err = NULL;
+
+ if (strcmp (op, "bts") == 0)
+ err = handle_btrace_enable (thread);
+ else if (strcmp (op, "off") == 0)
+ err = handle_btrace_disable (thread);
+ else
+ err = "E.Bad Qbtrace operation. Use bts or off.";
+
+ if (err != 0)
+ strcpy (own_buf, err);
+ else
+ write_ok (own_buf);
+
+ return 1;
+}
+
/* Handle all of the extended 'Q' packets. */
static void
return;
}
+ if (handle_btrace_general_set (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;
}
data-specific information to the target.
Return the number of bytes actually transfered, zero when no
- further transfer is possible, -1 on error, and -2 when the
- transfer is not supported. Return of a positive value smaller
- than LEN does not indicate the end of the object, only the end of
- the transfer.
+ further transfer is possible, -1 on error, -2 when the transfer
+ is not supported, and -3 on a verbose error message that should
+ be preserved. Return of a positive value smaller than LEN does
+ not indicate the end of the object, only the end of the transfer.
One, and only one, of readbuf or writebuf must be non-NULL. */
int (*xfer) (const char *annex,
return (*the_target->read_loadmap) (annex, offset, readbuf, len);
}
+/* Handle qXfer:btrace:read. */
+
+static int
+handle_qxfer_btrace (const char *annex,
+ gdb_byte *readbuf, const gdb_byte *writebuf,
+ ULONGEST offset, LONGEST len)
+{
+ static struct buffer cache;
+ struct thread_info *thread;
+ int type;
+
+ if (the_target->read_btrace == NULL || writebuf != NULL)
+ return -2;
+
+ if (!target_running ())
+ return -1;
+
+ if (ptid_equal (general_thread, null_ptid)
+ || ptid_equal (general_thread, minus_one_ptid))
+ {
+ strcpy (own_buf, "E.Must select a single thread.");
+ return -3;
+ }
+
+ thread = find_thread_ptid (general_thread);
+ if (thread == NULL)
+ {
+ strcpy (own_buf, "E.No such thread.");
+ return -3;
+ }
+
+ if (thread->btrace == NULL)
+ {
+ strcpy (own_buf, "E.Btrace not enabled.");
+ return -3;
+ }
+
+ if (strcmp (annex, "all") == 0)
+ type = btrace_read_all;
+ else if (strcmp (annex, "new") == 0)
+ type = btrace_read_new;
+ else
+ {
+ strcpy (own_buf, "E.Bad annex.");
+ return -3;
+ }
+
+ if (offset == 0)
+ {
+ buffer_free (&cache);
+
+ target_read_btrace (thread->btrace, &cache, type);
+ }
+ else if (offset > cache.used_size)
+ {
+ buffer_free (&cache);
+ return -3;
+ }
+
+ if (len > cache.used_size - offset)
+ len = cache.used_size - offset;
+
+ memcpy (readbuf, cache.buffer + offset, len);
+
+ return len;
+}
+
static const struct qxfer qxfer_packets[] =
{
{ "auxv", handle_qxfer_auxv },
+ { "btrace", handle_qxfer_btrace },
{ "fdpic", handle_qxfer_fdpic},
{ "features", handle_qxfer_features },
{ "libraries", handle_qxfer_libraries },
free (data);
return 0;
}
+ else if (n == -3)
+ {
+ /* Preserve error message. */
+ }
else if (n < 0)
write_enn (own_buf);
else if (n > len)
free (data);
return 0;
}
+ else if (n == -3)
+ {
+ /* Preserve error message. */
+ }
else if (n < 0)
write_enn (own_buf);
else
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+");
}
if (target_supports_agent ())
strcat (own_buf, ";QAgent+");
+ if (target_supports_btrace ())
+ {
+ strcat (own_buf, ";Qbtrace:bts+");
+ strcat (own_buf, ";Qbtrace:off+");
+ strcat (own_buf, ";qXfer:btrace:read+");
+ }
+
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);
{
p++;
+ memset (&resume_info[i], 0, sizeof resume_info[i]);
+
if (p[0] == 's' || p[0] == 'S')
resume_info[i].kind = resume_step;
+ else if (p[0] == 'r')
+ resume_info[i].kind = resume_step;
else if (p[0] == 'c' || p[0] == 'C')
resume_info[i].kind = resume_continue;
else if (p[0] == 't')
goto err;
resume_info[i].sig = gdb_signal_to_host (sig);
}
+ else if (p[0] == 'r')
+ {
+ char *p1;
+
+ p = p + 1;
+ p1 = strchr (p, ',');
+ decode_address (&resume_info[i].step_range_start, p, p1 - p);
+
+ p = p1 + 1;
+ p1 = strchr (p, ':');
+ decode_address (&resume_info[i].step_range_end, p, p1 - p);
+
+ p = p1;
+ }
else
{
- resume_info[i].sig = 0;
p = p + 1;
}
}
}
-/* 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)
if (strncmp (own_buf, "vCont?", 6) == 0)
{
strcpy (own_buf, "vCont;c;C;s;S;t");
+ if (target_supports_range_stepping ())
+ {
+ own_buf = own_buf + strlen (own_buf);
+ strcpy (own_buf, ";r");
+ }
return;
}
}
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
{
/* 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
{
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",
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. */
if (*dataptr == 'X')
{
/* Conditional expression. */
- fprintf (stderr, "Found breakpoint condition.\n");
+ if (debug_threads)
+ fprintf (stderr, "Found breakpoint condition.\n");
add_breakpoint_condition (point_addr, &dataptr);
}
else if (strncmp (dataptr, "cmds:", strlen ("cmds:")) == 0)
}
else
{
- /* Unrecognized token, just skip it. */
fprintf (stderr, "Unknown token %c, ignoring.\n",
*dataptr);
+ /* Skip tokens until we find one that we recognize. */
+ while (*dataptr && *dataptr != ';')
+ dataptr++;
}
-
- /* Skip tokens until we find one that we recognize. */
- while (*dataptr && *dataptr != 'X' && *dataptr != ';')
- dataptr++;
}
*packet = dataptr;
}
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) || any_persistent_commands ())
{
/* 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;
{
/* 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 ();
}
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);
}
}