2012-03-08 Stan Shebs <stan@codesourcery.com>
[deliverable/binutils-gdb.git] / gdb / gdbserver / server.c
index 9fd550b0971e48e56de99bf658e92907167af1f8..922a5bfa6e02b72779b8854f7ad7202462baba63 100644 (file)
@@ -1,6 +1,5 @@
 /* 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.
 
@@ -20,6 +19,7 @@
 #include "server.h"
 #include "gdbthread.h"
 #include "agent.h"
+#include "notif.h"
 
 #if HAVE_UNISTD_H
 #include <unistd.h>
@@ -27,9 +27,7 @@
 #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
@@ -117,13 +115,13 @@ static ptid_t last_ptid;
 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;
@@ -132,66 +130,39 @@ struct vstop_notif
   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 (&notif_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, &notif_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
@@ -200,40 +171,23 @@ push_event (ptid_t ptid, struct target_waitstatus *status)
 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,
-                         &notif_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)
 {
@@ -322,8 +276,6 @@ start_inferior (char **argv)
        }
       while (last_status.value.sig != GDB_SIGNAL_TRAP);
 
-      current_inferior->last_resume_kind = resume_stop;
-      current_inferior->last_status = last_status;
       return signal_pid;
     }
 
@@ -737,8 +689,9 @@ handle_search_memory_1 (CORE_ADDR start_addr, CORE_ADDR search_space_len,
   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;
     }
 
@@ -789,9 +742,9 @@ handle_search_memory_1 (CORE_ADDR start_addr, CORE_ADDR search_space_len,
          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;
            }
 
@@ -1684,11 +1637,13 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
          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.  */
+      /* Support target-side breakpoint conditions and commands.  */
       strcat (own_buf, ";ConditionalBreakpoints+");
+      strcat (own_buf, ";BreakpointCommands+");
 
       if (target_supports_agent ())
        strcat (own_buf, ";QAgent+");
@@ -1854,12 +1809,12 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
     {
       /* 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);
@@ -1974,8 +1929,8 @@ handle_v_cont (char *own_buf)
 
   /* `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 any form of wildcard
-     vCont.  */
+     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_get_lwp (resume_info[0].thread) == -1)
@@ -2171,29 +2126,6 @@ handle_v_kill (char *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)
@@ -2254,11 +2186,8 @@ 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.  */
@@ -2283,8 +2212,7 @@ myresume (char *own_buf, int step, int sig)
 
   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
@@ -2340,9 +2268,14 @@ queue_stop_reply_callback (struct inferior_list_entry *entry, void *arg)
      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 (&notif_stop,
+                        (struct notif_event *) new_notif);
     }
   else
     {
@@ -2423,7 +2356,7 @@ handle_status (char *own_buf)
       /* The first is sent immediatly.  OK is sent if there is no
         stopped thread, which is the same handling of the vStopped
         packet (by design).  */
-      send_next_stop_reply (own_buf);
+      notif_write_event (&notif_stop, own_buf);
     }
   else
     {
@@ -2449,7 +2382,7 @@ static void
 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",
@@ -2755,6 +2688,7 @@ main (int argc, char *argv[])
 
   initialize_async_io ();
   initialize_low ();
+  initialize_event_loop ();
   if (target_supports_tracepoints ())
     initialize_tracepoint ();
 
@@ -2792,6 +2726,8 @@ main (int argc, char *argv[])
       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.  */
@@ -2909,6 +2845,7 @@ 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 != ';')
@@ -2918,23 +2855,33 @@ process_point_options (CORE_ADDR point_addr, char **packet)
 
   while (*dataptr)
     {
-      switch (*dataptr)
+      if (*dataptr == ';')
+       ++dataptr;
+
+      if (*dataptr == 'X')
        {
-         case 'X':
-           /* Conditional expression.  */
-           if (remote_debug)
-             fprintf (stderr, "Found breakpoint condition.\n");
-           add_breakpoint_condition (point_addr, &dataptr);
-           break;
-         default:
-           /* Unrecognized token, just skip it.  */
-           fprintf (stderr, "Unknown token %c, ignoring.\n",
-                    *dataptr);
+         /* 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++;
        }
-
-      /* Skip tokens until we find one that we recognize.  */
-      while (*dataptr && *dataptr != 'X' && *dataptr != ';')
-       dataptr++;
     }
   *packet = dataptr;
 }
@@ -2996,10 +2943,9 @@ process_serial_event (void)
          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);
@@ -3010,9 +2956,15 @@ process_serial_event (void)
              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
@@ -3242,13 +3194,16 @@ process_serial_event (void)
       /* 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;
@@ -3388,7 +3343,7 @@ process_serial_event (void)
     {
       /* 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 ();
@@ -3486,8 +3441,14 @@ handle_target_event (int err, gdb_client_data client_data)
        }
       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 (&notif_stop,
+                     (struct notif_event *) vstop_notif);
        }
     }
 
This page took 0.030032 seconds and 4 git commands to generate.