Static tracepoints support, and UST integration.
[deliverable/binutils-gdb.git] / gdb / gdbserver / server.c
index 998fd0ce9a3c04ef866bc01e9517ca7d82914233..e666684182c4db560795cf2fcab282780ae8cca6 100644 (file)
@@ -1,6 +1,6 @@
 /* Main code for remote server for GDB.
    Copyright (C) 1989, 1993, 1994, 1995, 1997, 1998, 1999, 2000, 2002, 2003,
-   2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
+   2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
@@ -51,6 +51,9 @@ static char **program_argv, **wrapper_argv;
    was originally used to debug LinuxThreads support.  */
 int debug_threads;
 
+/* Enable debugging of h/w breakpoint/watchpoint support.  */
+int debug_hw_points;
+
 int pass_signals[TARGET_SIGNAL_LAST];
 
 jmp_buf toplevel;
@@ -238,6 +241,14 @@ start_inferior (char **argv)
       new_argv[count] = NULL;
     }
 
+  if (debug_threads)
+    {
+      int i;
+      for (i = 0; new_argv[i]; ++i)
+       fprintf (stderr, "new_argv[%d] = \"%s\"\n", i, new_argv[i]);
+      fflush (stderr);
+    }
+
 #ifdef SIGTTOU
   signal (SIGTTOU, SIG_DFL);
   signal (SIGTTIN, SIG_DFL);
@@ -368,7 +379,8 @@ write_qxfer_response (char *buf, const void *data, int len, int is_more)
 }
 
 /* Handle all of the extended 'Q' packets.  */
-void
+
+static void
 handle_general_set (char *own_buf)
 {
   if (strncmp ("QPassSignals:", own_buf, strlen ("QPassSignals:")) == 0)
@@ -446,6 +458,10 @@ handle_general_set (char *own_buf)
       return;
     }
 
+  if (target_supports_tracepoints ()
+      && handle_tracepoint_general_set (own_buf))
+    return;
+
   /* Otherwise we didn't know what packet it was.  Say we didn't
      understand it.  */
   own_buf[0] = 0;
@@ -495,12 +511,51 @@ monitor_show_help (void)
   monitor_output ("The following monitor commands are supported:\n");
   monitor_output ("  set debug <0|1>\n");
   monitor_output ("    Enable general debugging messages\n");
+  monitor_output ("  set debug-hw-points <0|1>\n");
+  monitor_output ("    Enable h/w breakpoint/watchpoint debugging messages\n");
   monitor_output ("  set remote-debug <0|1>\n");
   monitor_output ("    Enable remote protocol debugging messages\n");
   monitor_output ("  exit\n");
   monitor_output ("    Quit GDBserver\n");
 }
 
+/* Read trace frame or inferior memory.  */
+
+static int
+read_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len)
+{
+  if (current_traceframe >= 0)
+    {
+      ULONGEST nbytes;
+      ULONGEST length = len;
+
+      if (traceframe_read_mem (current_traceframe,
+                              memaddr, myaddr, len, &nbytes))
+       return EIO;
+      /* Data read from trace buffer, we're done.  */
+      if (nbytes == length)
+       return 0;
+      if (!in_readonly_region (memaddr, length))
+       return EIO;
+      /* Otherwise we have a valid readonly case, fall through.  */
+      /* (assume no half-trace half-real blocks for now) */
+    }
+
+  return read_inferior_memory (memaddr, myaddr, len);
+}
+
+/* Write trace frame or inferior memory.  Actually, writing to trace
+   frames is forbidden.  */
+
+static int
+write_memory (CORE_ADDR memaddr, const unsigned char *myaddr, int len)
+{
+  if (current_traceframe >= 0)
+    return EIO;
+  else
+    return write_inferior_memory (memaddr, myaddr, len);
+}
+
 /* Subroutine of handle_search_memory to simplify it.  */
 
 static int
@@ -512,7 +567,7 @@ handle_search_memory_1 (CORE_ADDR start_addr, CORE_ADDR search_space_len,
 {
   /* Prime the search buffer.  */
 
-  if (read_inferior_memory (start_addr, search_buf, search_buf_size) != 0)
+  if (read_memory (start_addr, search_buf, search_buf_size) != 0)
     {
       warning ("Unable to access target memory at 0x%lx, halting search.",
               (long) start_addr);
@@ -552,7 +607,7 @@ handle_search_memory_1 (CORE_ADDR start_addr, CORE_ADDR search_space_len,
       if (search_space_len >= pattern_len)
        {
          unsigned keep_len = search_buf_size - chunk_size;
-         CORE_ADDR read_addr = start_addr + keep_len;
+         CORE_ADDR read_addr = start_addr + chunk_size + keep_len;
          int nr_to_read;
 
          /* Copy the trailing part of the previous iteration to the front
@@ -563,7 +618,7 @@ handle_search_memory_1 (CORE_ADDR start_addr, CORE_ADDR search_space_len,
                        ? search_space_len - keep_len
                        : chunk_size);
 
-         if (read_inferior_memory (read_addr, search_buf + keep_len,
+         if (read_memory (read_addr, search_buf + keep_len,
                                    nr_to_read) != 0)
            {
              warning ("Unable to access target memory at 0x%lx, halting search.",
@@ -655,6 +710,175 @@ handle_search_memory (char *own_buf, int packet_len)
       return;                                  \
     }
 
+/* Handle monitor commands not handled by target-specific handlers.  */
+
+static void
+handle_monitor_command (char *mon)
+{
+  if (strcmp (mon, "set debug 1") == 0)
+    {
+      debug_threads = 1;
+      monitor_output ("Debug output enabled.\n");
+    }
+  else if (strcmp (mon, "set debug 0") == 0)
+    {
+      debug_threads = 0;
+      monitor_output ("Debug output disabled.\n");
+    }
+  else if (strcmp (mon, "set debug-hw-points 1") == 0)
+    {
+      debug_hw_points = 1;
+      monitor_output ("H/W point debugging output enabled.\n");
+    }
+  else if (strcmp (mon, "set debug-hw-points 0") == 0)
+    {
+      debug_hw_points = 0;
+      monitor_output ("H/W point debugging output disabled.\n");
+    }
+  else if (strcmp (mon, "set remote-debug 1") == 0)
+    {
+      remote_debug = 1;
+      monitor_output ("Protocol debug output enabled.\n");
+    }
+  else if (strcmp (mon, "set remote-debug 0") == 0)
+    {
+      remote_debug = 0;
+      monitor_output ("Protocol debug output disabled.\n");
+    }
+  else if (strcmp (mon, "help") == 0)
+    monitor_show_help ();
+  else if (strcmp (mon, "exit") == 0)
+    exit_requested = 1;
+  else
+    {
+      monitor_output ("Unknown monitor command.\n\n");
+      monitor_show_help ();
+      write_enn (own_buf);
+    }
+}
+
+static void
+handle_threads_qxfer_proper (struct buffer *buffer)
+{
+  struct inferior_list_entry *thread;
+
+  buffer_grow_str (buffer, "<threads>\n");
+
+  for (thread = all_threads.head; thread; thread = thread->next)
+    {
+      ptid_t ptid = thread_to_gdb_id ((struct thread_info *)thread);
+      char ptid_s[100];
+      int core = -1;
+      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);
+         buffer_xml_printf (buffer, "<thread id=\"%s\" core=\"%s\"/>\n",
+                            ptid_s, core_s);
+       }
+      else
+       {
+         buffer_xml_printf (buffer, "<thread id=\"%s\"/>\n",
+                            ptid_s);
+       }
+    }
+
+  buffer_grow_str0 (buffer, "</threads>\n");
+}
+
+static int
+handle_threads_qxfer (const char *annex,
+                     unsigned char *readbuf,
+                     CORE_ADDR offset, int length)
+{
+  static char *result = 0;
+  static unsigned int result_length = 0;
+
+  if (annex && strcmp (annex, "") != 0)
+    return 0;
+
+  if (offset == 0)
+    {
+      struct buffer buffer;
+      /* When asked for data at offset 0, generate everything and store into
+        'result'.  Successive reads will be served off 'result'.  */
+      if (result)
+       free (result);
+
+      buffer_init (&buffer);
+
+      handle_threads_qxfer_proper (&buffer);
+
+      result = buffer_finish (&buffer);
+      result_length = strlen (result);
+      buffer_free (&buffer);
+    }
+
+  if (offset >= result_length)
+    {
+      /* We're out of data.  */
+      free (result);
+      result = NULL;
+      result_length = 0;
+      return 0;
+    }
+
+  if (length > result_length - offset)
+    length = result_length - offset;
+
+  memcpy (readbuf, result + offset, length);
+
+  return length;
+
+}
+
+/* Table used by the crc32 function to calcuate the checksum.  */
+
+static unsigned int crc32_table[256] =
+{0, 0};
+
+/* Compute 32 bit CRC from inferior memory.
+
+   On success, return 32 bit CRC.
+   On failure, return (unsigned long long) -1.  */
+
+static unsigned long long
+crc32 (CORE_ADDR base, int len, unsigned int crc)
+{
+  if (!crc32_table[1])
+    {
+      /* Initialize the CRC table and the decoding table.  */
+      int i, j;
+      unsigned int c;
+
+      for (i = 0; i < 256; i++)
+       {
+         for (c = i << 24, j = 8; j > 0; --j)
+           c = c & 0x80000000 ? (c << 1) ^ 0x04c11db7 : (c << 1);
+         crc32_table[i] = c;
+       }
+    }
+
+  while (len--)
+    {
+      unsigned char byte = 0;
+
+      /* Return failure if memory read fails.  */
+      if (read_inferior_memory (base, &byte, 1) != 0)
+       return (unsigned long long) -1;
+
+      crc = (crc << 8) ^ crc32_table[((crc >> 24) ^ byte) & 255];
+      base++;
+    }
+  return (unsigned long long) crc;
+}
+
 /* Handle all of the extended 'q' packets.  */
 void
 handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
@@ -684,6 +908,21 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
 
   if (strcmp ("qSymbol::", own_buf) == 0)
     {
+      /* GDB is suggesting new symbols have been loaded.  This may
+        mean a new shared library has been detected as loaded, so
+        take the opportunity to check if breakpoints we think are
+        inserted, still are.  Note that it isn't guaranteed that
+        we'll see this when a shared library is loaded, and nor will
+        we see this for unloads (although breakpoints in unloaded
+        libraries shouldn't trigger), as GDB may not find symbols for
+        the library at all.  We also re-validate breakpoints when we
+        see a second GDB breakpoint for the same address, and or when
+        we access breakpoint shadows.  */
+      validate_breakpoints ();
+
+      if (target_supports_tracepoints ())
+       tracepoint_look_up_symbols ();
+
       if (target_running () && the_target->look_up_symbols != NULL)
        (*the_target->look_up_symbols) ();
 
@@ -1060,24 +1299,142 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
       return;
     }
 
+  if (strncmp ("qXfer:threads:read:", own_buf, 19) == 0)
+    {
+      unsigned char *data;
+      int n;
+      CORE_ADDR ofs;
+      unsigned int len;
+      char *annex;
+
+      require_running (own_buf);
+
+      /* Reject any annex; grab the offset and length.  */
+      if (decode_xfer_read (own_buf + 19, &annex, &ofs, &len) < 0
+         || annex[0] != '\0')
+       {
+         strcpy (own_buf, "E00");
+         return;
+       }
+
+      /* Read one extra byte, as an indicator of whether there is
+        more.  */
+      if (len > PBUFSIZ - 2)
+       len = PBUFSIZ - 2;
+      data = malloc (len + 1);
+      if (!data)
+       return;
+      n = handle_threads_qxfer (annex, data, ofs, len + 1);
+      if (n < 0)
+       write_enn (own_buf);
+      else if (n > len)
+       *new_packet_len_p = write_qxfer_response (own_buf, data, len, 1);
+      else
+       *new_packet_len_p = write_qxfer_response (own_buf, data, n, 0);
+
+      free (data);
+      return;
+    }
+
+  if (strncmp ("qXfer:statictrace:read:", own_buf,
+              sizeof ("qXfer:statictrace:read:") -1) == 0)
+    {
+      unsigned char *data;
+      CORE_ADDR ofs;
+      unsigned int len;
+      char *annex;
+      ULONGEST nbytes;
+
+      require_running (own_buf);
+
+      if (current_traceframe == -1)
+       {
+         write_enn (own_buf);
+         return;
+       }
+
+      /* Reject any annex; grab the offset and length.  */
+      if (decode_xfer_read (own_buf + sizeof ("qXfer:statictrace:read:") -1,
+                           &annex, &ofs, &len) < 0
+         || annex[0] != '\0')
+       {
+         strcpy (own_buf, "E00");
+         return;
+       }
+
+      /* Read one extra byte, as an indicator of whether there is
+        more.  */
+      if (len > PBUFSIZ - 2)
+       len = PBUFSIZ - 2;
+      data = malloc (len + 1);
+      if (!data)
+       return;
+
+      if (traceframe_read_sdata (current_traceframe, ofs,
+                                data, len + 1, &nbytes))
+       write_enn (own_buf);
+      else if (nbytes > len)
+       *new_packet_len_p = write_qxfer_response (own_buf, data, len, 1);
+      else
+       *new_packet_len_p = write_qxfer_response (own_buf, data, nbytes, 0);
+
+      free (data);
+      return;
+    }
+
   /* Protocol features query.  */
   if (strncmp ("qSupported", own_buf, 10) == 0
       && (own_buf[10] == ':' || own_buf[10] == '\0'))
     {
       char *p = &own_buf[10];
+      int gdb_supports_qRelocInsn = 0;
+
+      /* Start processing qSupported packet.  */
+      target_process_qsupported (NULL);
 
       /* Process each feature being provided by GDB.  The first
         feature will follow a ':', and latter features will follow
         ';'.  */
       if (*p == ':')
-       for (p = strtok (p + 1, ";");
-            p != NULL;
-            p = strtok (NULL, ";"))
-         {
-           /* Record if GDB knows about multiprocess support.  */
-           if (strcmp (p, "multiprocess+") == 0)
-             multi_process = 1;
-         }
+       {
+         char **qsupported = NULL;
+         int count = 0;
+         int i;
+
+         /* Two passes, to avoid nested strtok calls in
+            target_process_qsupported.  */
+         for (p = strtok (p + 1, ";");
+              p != NULL;
+              p = strtok (NULL, ";"))
+           {
+             count++;
+             qsupported = xrealloc (qsupported, count * sizeof (char *));
+             qsupported[count - 1] = xstrdup (p);
+           }
+
+         for (i = 0; i < count; i++)
+           {
+             p = qsupported[i];
+             if (strcmp (p, "multiprocess+") == 0)
+               {
+                 /* GDB supports and wants multi-process support if
+                    possible.  */
+                 if (target_supports_multi_process ())
+                   multi_process = 1;
+               }
+             else if (strcmp (p, "qRelocInsn+") == 0)
+               {
+                 /* GDB supports relocate instruction requests.  */
+                 gdb_supports_qRelocInsn = 1;
+               }
+             else
+               target_process_qsupported (p);
+
+             free (p);
+           }
+
+         free (qsupported);
+       }
 
       sprintf (own_buf, "PacketSize=%x;QPassSignals+", PBUFSIZ - 1);
 
@@ -1106,11 +1463,26 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
       if (the_target->qxfer_osdata != NULL)
        strcat (own_buf, ";qXfer:osdata:read+");
 
-      strcat (own_buf, ";multiprocess+");
+      if (target_supports_multi_process ())
+       strcat (own_buf, ";multiprocess+");
 
       if (target_supports_non_stop ())
        strcat (own_buf, ";QNonStop+");
 
+      strcat (own_buf, ";qXfer:threads:read+");
+
+      if (target_supports_tracepoints ())
+       {
+         strcat (own_buf, ";ConditionalTracepoints+");
+         strcat (own_buf, ";TraceStateVariables+");
+         strcat (own_buf, ";TracepointSource+");
+         strcat (own_buf, ";DisconnectedTracing+");
+         if (gdb_supports_qRelocInsn && target_supports_fast_tracepoints ())
+           strcat (own_buf, ";FastTracepoints+");
+         strcat (own_buf, ";StaticTracepoints+");
+         strcat (own_buf, ";qXfer:statictrace:read+");
+       }
+
       return;
     }
 
@@ -1156,7 +1528,7 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
        err = 1;
       else
        {
-         struct thread_info *thread = find_thread_pid (ptid);
+         struct thread_info *thread = find_thread_ptid (ptid);
 
          if (thread == NULL)
            err = 2;
@@ -1179,6 +1551,29 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
       /* Otherwise, pretend we do not understand this packet.  */
     }
 
+  /* Windows OS Thread Information Block address support.  */
+  if (the_target->get_tib_address != NULL
+      && strncmp ("qGetTIBAddr:", own_buf, 12) == 0)
+    {
+      char *annex;
+      int n;
+      CORE_ADDR tlb;
+      ptid_t ptid = read_ptid (own_buf + 12, &annex);
+
+      n = (*the_target->get_tib_address) (ptid, &tlb);
+      if (n == 1)
+       {
+         sprintf (own_buf, "%llx", tlb);
+         return;
+       }
+      else if (n == 0)
+       {
+         write_enn (own_buf);
+         return;
+       }
+      return;
+    }
+
   /* Handle "monitor" commands.  */
   if (strncmp ("qRcmd,", own_buf, 6) == 0)
     {
@@ -1201,36 +1596,10 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
 
       write_ok (own_buf);
 
-      if (strcmp (mon, "set debug 1") == 0)
-       {
-         debug_threads = 1;
-         monitor_output ("Debug output enabled.\n");
-       }
-      else if (strcmp (mon, "set debug 0") == 0)
-       {
-         debug_threads = 0;
-         monitor_output ("Debug output disabled.\n");
-       }
-      else if (strcmp (mon, "set remote-debug 1") == 0)
-       {
-         remote_debug = 1;
-         monitor_output ("Protocol debug output enabled.\n");
-       }
-      else if (strcmp (mon, "set remote-debug 0") == 0)
-       {
-         remote_debug = 0;
-         monitor_output ("Protocol debug output disabled.\n");
-       }
-      else if (strcmp (mon, "help") == 0)
-       monitor_show_help ();
-      else if (strcmp (mon, "exit") == 0)
-       exit_requested = 1;
-      else
-       {
-         monitor_output ("Unknown monitor command.\n\n");
-         monitor_show_help ();
-         write_enn (own_buf);
-       }
+      if (the_target->handle_monitor_command == NULL
+         || (*the_target->handle_monitor_command) (mon) == 0)
+       /* Default processing.  */
+       handle_monitor_command (mon);
 
       free (mon);
       return;
@@ -1270,6 +1639,36 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
       return;
     }
 
+  if (strncmp ("qCRC:", own_buf, 5) == 0)
+    {
+      /* CRC check (compare-section).  */
+      char *comma;
+      CORE_ADDR base;
+      int len;
+      unsigned long long crc;
+
+      require_running (own_buf);
+      base = strtoul (own_buf + 5, &comma, 16);
+      if (*comma++ != ',')
+       {
+         write_enn (own_buf);
+         return;
+       }
+      len = strtoul (comma, NULL, 16);
+      crc = crc32 (base, len, 0xffffffff);
+      /* Check for memory failure.  */
+      if (crc == (unsigned long long) -1)
+       {
+         write_enn (own_buf);
+         return;
+       }
+      sprintf (own_buf, "C%lx", (unsigned long) crc);
+      return;
+    }
+
+  if (target_supports_tracepoints () && handle_tracepoint_query (own_buf))
+    return;
+
   /* Otherwise we didn't know what packet it was.  Say we didn't
      understand it.  */
   own_buf[0] = 0;
@@ -1518,8 +1917,10 @@ handle_v_kill (char *own_buf)
 {
   int pid;
   char *p = &own_buf[6];
-
-  pid = strtol (p, NULL, 16);
+  if (multi_process)
+    pid = strtol (p, NULL, 16);
+  else
+    pid = signal_pid;
   if (pid != 0 && kill_inferior (pid) == 0)
     {
       last_status.kind = TARGET_WAITKIND_SIGNALLED;
@@ -1687,31 +2088,81 @@ myresume (char *own_buf, int step, int sig)
 static int
 queue_stop_reply_callback (struct inferior_list_entry *entry, void *arg)
 {
-  int pid = * (int *) arg;
+  struct thread_info *thread = (struct thread_info *) entry;
 
-  if (pid == -1
-      || ptid_get_pid (entry->id) == pid)
+  /* For now, assume targets that don't have this callback also don't
+     manage the thread's last_status field.  */
+  if (the_target->thread_stopped == NULL)
     {
       struct target_waitstatus status;
 
       status.kind = TARGET_WAITKIND_STOPPED;
       status.value.sig = TARGET_SIGNAL_TRAP;
 
-      /* Pass the last stop reply back to GDB, but don't notify.  */
-      queue_stop_reply (entry->id, &status);
+      /* Pass the last stop reply back to GDB, but don't notify
+        yet.  */
+      queue_stop_reply (entry->id, &thread->last_status);
+    }
+  else
+    {
+      if (thread_stopped (thread))
+       {
+         if (debug_threads)
+           fprintf (stderr, "Reporting thread %s as already stopped with %s\n",
+                    target_pid_to_str (entry->id),
+                    target_waitstatus_to_string (&thread->last_status));
+
+         /* Pass the last stop reply back to GDB, but don't notify
+            yet.  */
+         queue_stop_reply (entry->id, &thread->last_status);
+       }
     }
 
   return 0;
 }
 
+/* Set this inferior LWP's state as "want-stopped".  We won't resume
+   this LWP until the client gives us another action for it.  */
+
+static void
+gdb_wants_thread_stopped (struct inferior_list_entry *entry)
+{
+  struct thread_info *thread = (struct thread_info *) entry;
+
+  thread->last_resume_kind = resume_stop;
+
+  if (thread->last_status.kind == TARGET_WAITKIND_IGNORE)
+    {
+      thread->last_status.kind = TARGET_WAITKIND_STOPPED;
+      thread->last_status.value.sig = TARGET_SIGNAL_0;
+    }
+}
+
+/* Set all threads' states as "want-stopped".  */
+
+static void
+gdb_wants_all_threads_stopped (void)
+{
+  for_each_inferior (&all_threads, gdb_wants_thread_stopped);
+}
+
+/* Clear the gdb_detached flag of every process.  */
+
+static void
+gdb_reattached_process (struct inferior_list_entry *entry)
+{
+  struct process_info *process = (struct process_info *) entry;
+
+  process->gdb_detached = 0;
+}
+
 /* Status handler for the '?' packet.  */
 
 static void
 handle_status (char *own_buf)
 {
-  struct target_waitstatus status;
-  status.kind = TARGET_WAITKIND_STOPPED;
-  status.value.sig = TARGET_SIGNAL_TRAP;
+  /* GDB is connected, don't forward events to the target anymore.  */
+  for_each_inferior (&all_processes, gdb_reattached_process);
 
   /* In non-stop mode, we must send a stop reply for each stopped
      thread.  In all-stop mode, just send one for the first stopped
@@ -1719,9 +2170,8 @@ handle_status (char *own_buf)
 
   if (non_stop)
     {
-      int pid = -1;
-      discard_queued_stop_replies (pid);
-      find_inferior (&all_threads, queue_stop_reply_callback, &pid);
+      discard_queued_stop_replies (-1);
+      find_inferior (&all_threads, queue_stop_reply_callback, NULL);
 
       /* The first is sent immediatly.  OK is sent if there is no
         stopped thread, which is the same handling of the vStopped
@@ -1730,9 +2180,19 @@ handle_status (char *own_buf)
     }
   else
     {
+      pause_all (0);
+      stabilize_threads ();
+      gdb_wants_all_threads_stopped ();
+
       if (all_threads.head)
-       prepare_resume_reply (own_buf,
-                             all_threads.head->id, &status);
+       {
+         struct target_waitstatus status;
+
+         status.kind = TARGET_WAITKIND_STOPPED;
+         status.value.sig = TARGET_SIGNAL_TRAP;
+         prepare_resume_reply (own_buf,
+                               all_threads.head->id, &status);
+       }
       else
        strcpy (own_buf, "W00");
     }
@@ -1742,7 +2202,7 @@ static void
 gdbserver_version (void)
 {
   printf ("GNU gdbserver %s%s\n"
-         "Copyright (C) 2009 Free Software Foundation, Inc.\n"
+         "Copyright (C) 2010 Free Software Foundation, Inc.\n"
          "gdbserver is free software, covered by the GNU General Public License.\n"
          "This gdbserver was configured as \"%s\"\n",
          PKGVERSION, version, host_name);
@@ -1808,6 +2268,11 @@ kill_inferior_callback (struct inferior_list_entry *entry)
   discard_queued_stop_replies (pid);
 }
 
+/* Callback for for_each_inferior to detach or kill the inferior,
+   depending on whether we attached to it or not.
+   We inform the user whether we're detaching or killing the process
+   as this is only called when gdbserver is about to exit.  */
+
 static void
 detach_or_kill_inferior_callback (struct inferior_list_entry *entry)
 {
@@ -1822,6 +2287,65 @@ detach_or_kill_inferior_callback (struct inferior_list_entry *entry)
   discard_queued_stop_replies (pid);
 }
 
+/* for_each_inferior callback for detach_or_kill_for_exit to print
+   the pids of started inferiors.  */
+
+static void
+print_started_pid (struct inferior_list_entry *entry)
+{
+  struct process_info *process = (struct process_info *) entry;
+
+  if (! process->attached)
+    {
+      int pid = ptid_get_pid (process->head.id);
+      fprintf (stderr, " %d", pid);
+    }
+}
+
+/* for_each_inferior callback for detach_or_kill_for_exit to print
+   the pids of attached inferiors.  */
+
+static void
+print_attached_pid (struct inferior_list_entry *entry)
+{
+  struct process_info *process = (struct process_info *) entry;
+
+  if (process->attached)
+    {
+      int pid = ptid_get_pid (process->head.id);
+      fprintf (stderr, " %d", pid);
+    }
+}
+
+/* Call this when exiting gdbserver with possible inferiors that need
+   to be killed or detached from.  */
+
+static void
+detach_or_kill_for_exit (void)
+{
+  /* First print a list of the inferiors we will be killing/detaching.
+     This is to assist the user, for example, in case the inferior unexpectedly
+     dies after we exit: did we screw up or did the inferior exit on its own?
+     Having this info will save some head-scratching.  */
+
+  if (have_started_inferiors_p ())
+    {
+      fprintf (stderr, "Killing process(es):");
+      for_each_inferior (&all_processes, print_started_pid);
+      fprintf (stderr, "\n");
+    }
+  if (have_attached_inferiors_p ())
+    {
+      fprintf (stderr, "Detaching process(es):");
+      for_each_inferior (&all_processes, print_attached_pid);
+      fprintf (stderr, "\n");
+    }
+
+  /* Now we can kill or detach the inferiors.  */
+
+  for_each_inferior (&all_processes, detach_or_kill_inferior_callback);
+}
+
 static void
 join_inferiors_callback (struct inferior_list_entry *entry)
 {
@@ -1973,6 +2497,8 @@ main (int argc, char *argv[])
   initialize_inferiors ();
   initialize_async_io ();
   initialize_low ();
+  if (target_supports_tracepoints ())
+    initialize_tracepoint ();
 
   own_buf = xmalloc (PBUFSIZ + 1);
   mem_buf = xmalloc (PBUFSIZ);
@@ -2015,9 +2541,7 @@ main (int argc, char *argv[])
 
   if (setjmp (toplevel))
     {
-      fprintf (stderr, "Killing all inferiors\n");
-      for_each_inferior (&all_processes,
-                        kill_inferior_callback);
+      detach_or_kill_for_exit ();
       exit (1);
     }
 
@@ -2037,7 +2561,8 @@ main (int argc, char *argv[])
     {
       noack_mode = 0;
       multi_process = 0;
-      non_stop = 0;
+      /* Be sure we're out of tfind mode.  */
+      current_traceframe = -1;
 
       remote_open (port);
 
@@ -2052,7 +2577,7 @@ main (int argc, char *argv[])
        }
 
       /* Wait for events.  This will return when all event sources are
-        removed from the event loop. */
+        removed from the event loop.  */
       start_event_loop ();
 
       /* If an exit was requested (using the "monitor exit" command),
@@ -2062,13 +2587,40 @@ main (int argc, char *argv[])
 
       if (exit_requested)
        {
-         for_each_inferior (&all_processes,
-                            detach_or_kill_inferior_callback);
+         detach_or_kill_for_exit ();
          exit (0);
        }
-      else
-       fprintf (stderr, "Remote side has terminated connection.  "
-                "GDBserver will reopen the connection.\n");
+
+      fprintf (stderr,
+              "Remote side has terminated connection.  "
+              "GDBserver will reopen the connection.\n");
+
+      if (tracing)
+       {
+         if (disconnected_tracing)
+           {
+             /* Try to enable non-stop/async mode, so we we can both
+                wait for an async socket accept, and handle async
+                target events simultaneously.  There's also no point
+                either in having the target always stop all threads,
+                when we're going to pass signals down without
+                informing GDB.  */
+             if (!non_stop)
+               {
+                 if (start_non_stop (1))
+                   non_stop = 1;
+
+                 /* Detaching implicitly resumes all threads; simply
+                    disconnecting does not.  */
+               }
+           }
+         else
+           {
+             fprintf (stderr,
+                      "Disconnected tracing disabled; stopping trace run.\n");
+             stop_tracing ();
+           }
+       }
     }
 }
 
@@ -2077,7 +2629,7 @@ main (int argc, char *argv[])
    a brisk pace, so we read the rest of the packet with a blocking
    getpkt call.  */
 
-static void
+static int
 process_serial_event (void)
 {
   char ch;
@@ -2103,9 +2655,9 @@ process_serial_event (void)
   packet_len = getpkt (own_buf);
   if (packet_len <= 0)
     {
-      target_async (0);
       remote_close ();
-      return;
+      /* Force an event loop break.  */
+      return -1;
     }
   response_needed = 1;
 
@@ -2131,7 +2683,49 @@ process_serial_event (void)
        pid =
          ptid_get_pid (((struct inferior_list_entry *) current_inferior)->id);
 
+      if (tracing && disconnected_tracing)
+       {
+         struct thread_resume resume_info;
+         struct process_info *process = find_process_pid (pid);
+
+         if (process == NULL)
+           {
+             write_enn (own_buf);
+             break;
+           }
+
+         fprintf (stderr,
+                  "Disconnected tracing in effect, "
+                  "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
+            events simultaneously.  There's also no point either in
+            having the target stop all threads, when we're going to
+            pass signals down without informing GDB.  */
+         if (!non_stop)
+           {
+             if (debug_threads)
+               fprintf (stderr, "Forcing non-stop mode\n");
+
+             non_stop = 1;
+             start_non_stop (1);
+           }
+
+         process->gdb_detached = 1;
+
+         /* Detaching implicitly resumes all threads.  */
+         resume_info.thread = minus_one_ptid;
+         resume_info.kind = resume_continue;
+         resume_info.sig = 0;
+         (*the_target->resume) (&resume_info, 1);
+
+         write_ok (own_buf);
+         break; /* from switch/case */
+       }
+
       fprintf (stderr, "Detaching from process %d\n", pid);
+      stop_tracing ();
       if (detach_inferior (pid) != 0)
        write_enn (own_buf);
       else
@@ -2243,27 +2837,52 @@ process_serial_event (void)
       break;
     case 'g':
       require_running (own_buf);
-      set_desired_inferior (1);
-      registers_to_string (own_buf);
+      if (current_traceframe >= 0)
+       {
+         struct regcache *regcache = new_register_cache ();
+
+         if (fetch_traceframe_registers (current_traceframe,
+                                         regcache, -1) == 0)
+           registers_to_string (regcache, own_buf);
+         else
+           write_enn (own_buf);
+         free_register_cache (regcache);
+       }
+      else
+       {
+         struct regcache *regcache;
+
+         set_desired_inferior (1);
+         regcache = get_thread_regcache (current_inferior, 1);
+         registers_to_string (regcache, own_buf);
+       }
       break;
     case 'G':
       require_running (own_buf);
-      set_desired_inferior (1);
-      registers_from_string (&own_buf[1]);
-      write_ok (own_buf);
+      if (current_traceframe >= 0)
+       write_enn (own_buf);
+      else
+       {
+         struct regcache *regcache;
+
+         set_desired_inferior (1);
+         regcache = get_thread_regcache (current_inferior, 1);
+         registers_from_string (regcache, &own_buf[1]);
+         write_ok (own_buf);
+       }
       break;
     case 'm':
       require_running (own_buf);
       decode_m_packet (&own_buf[1], &mem_addr, &len);
-      if (read_inferior_memory (mem_addr, mem_buf, len) == 0)
+      if (read_memory (mem_addr, mem_buf, len) == 0)
        convert_int_to_ascii (mem_buf, own_buf, len);
       else
        write_enn (own_buf);
       break;
     case 'M':
       require_running (own_buf);
-      decode_M_packet (&own_buf[1], &mem_addr, &len, mem_buf);
-      if (write_inferior_memory (mem_addr, mem_buf, len) == 0)
+      decode_M_packet (&own_buf[1], &mem_addr, &len, &mem_buf);
+      if (write_memory (mem_addr, mem_buf, len) == 0)
        write_ok (own_buf);
       else
        write_enn (own_buf);
@@ -2271,8 +2890,8 @@ process_serial_event (void)
     case 'X':
       require_running (own_buf);
       if (decode_X_packet (&own_buf[1], packet_len - 1,
-                          &mem_addr, &len, mem_buf) < 0
-         || write_inferior_memory (mem_addr, mem_buf, len) != 0)
+                          &mem_addr, &len, &mem_buf) < 0
+         || write_memory (mem_addr, mem_buf, len) != 0)
        write_enn (own_buf);
       else
        write_ok (own_buf);
@@ -2305,66 +2924,44 @@ process_serial_event (void)
       signal = 0;
       myresume (own_buf, 1, signal);
       break;
-    case 'Z':
+    case 'Z':  /* insert_ ... */
+      /* Fallthrough.  */
+    case 'z':  /* remove_ ... */
       {
        char *lenptr;
        char *dataptr;
        CORE_ADDR addr = strtoul (&own_buf[3], &lenptr, 16);
        int len = strtol (lenptr + 1, &dataptr, 16);
        char type = own_buf[1];
+       int res;
+       const int insert = ch == 'Z';
 
-       if (the_target->insert_watchpoint == NULL
-           || (type < '2' || type > '4'))
+       /* Default to unrecognized/unsupported.  */
+       res = 1;
+       switch (type)
          {
-           /* No watchpoint support or not a watchpoint command;
-              unrecognized either way.  */
-           own_buf[0] = '\0';
-         }
-       else
-         {
-           int res;
-
+         case '0': /* software-breakpoint */
+         case '1': /* hardware-breakpoint */
+         case '2': /* write watchpoint */
+         case '3': /* read watchpoint */
+         case '4': /* access watchpoint */
            require_running (own_buf);
-           res = (*the_target->insert_watchpoint) (type, addr, len);
-           if (res == 0)
-             write_ok (own_buf);
-           else if (res == 1)
-             /* Unsupported.  */
-             own_buf[0] = '\0';
-           else
-             write_enn (own_buf);
+           if (insert && the_target->insert_point != NULL)
+             res = (*the_target->insert_point) (type, addr, len);
+           else if (!insert && the_target->remove_point != NULL)
+             res = (*the_target->remove_point) (type, addr, len);
+           break;
+         default:
+           break;
          }
-       break;
-      }
-    case 'z':
-      {
-       char *lenptr;
-       char *dataptr;
-       CORE_ADDR addr = strtoul (&own_buf[3], &lenptr, 16);
-       int len = strtol (lenptr + 1, &dataptr, 16);
-       char type = own_buf[1];
 
-       if (the_target->remove_watchpoint == NULL
-           || (type < '2' || type > '4'))
-         {
-           /* No watchpoint support or not a watchpoint command;
-              unrecognized either way.  */
-           own_buf[0] = '\0';
-         }
+       if (res == 0)
+         write_ok (own_buf);
+       else if (res == 1)
+         /* Unsupported.  */
+         own_buf[0] = '\0';
        else
-         {
-           int res;
-
-           require_running (own_buf);
-           res = (*the_target->remove_watchpoint) (type, addr, len);
-           if (res == 0)
-             write_ok (own_buf);
-           else if (res == 1)
-             /* Unsupported.  */
-             own_buf[0] = '\0';
-           else
-             write_enn (own_buf);
-         }
+         write_enn (own_buf);
        break;
       }
     case 'k':
@@ -2372,7 +2969,7 @@ process_serial_event (void)
       if (!target_running ())
        /* The packet we received doesn't make sense - but we can't
           reply to it, either.  */
-       return;
+       return 0;
 
       fprintf (stderr, "Killing all inferiors\n");
       for_each_inferior (&all_processes, kill_inferior_callback);
@@ -2383,13 +2980,11 @@ process_serial_event (void)
        {
          last_status.kind = TARGET_WAITKIND_EXITED;
          last_status.value.sig = TARGET_SIGNAL_KILL;
-         return;
+         return 0;
        }
       else
-       {
-         exit (0);
-         break;
-       }
+       exit (0);
+
     case 'T':
       {
        ptid_t gdb_id, thread_id;
@@ -2430,7 +3025,7 @@ process_serial_event (void)
              last_status.kind = TARGET_WAITKIND_EXITED;
              last_status.value.sig = TARGET_SIGNAL_KILL;
            }
-         return;
+         return 0;
        }
       else
        {
@@ -2471,27 +3066,35 @@ process_serial_event (void)
          exit (0);
        }
     }
+
+  if (exit_requested)
+    return -1;
+
+  return 0;
 }
 
 /* Event-loop callback for serial events.  */
 
-void
+int
 handle_serial_event (int err, gdb_client_data client_data)
 {
   if (debug_threads)
     fprintf (stderr, "handling possible serial event\n");
 
   /* Really handle it.  */
-  process_serial_event ();
+  if (process_serial_event () < 0)
+    return -1;
 
   /* Be sure to not change the selected inferior behind GDB's back.
      Important in the non-stop mode asynchronous protocol.  */
   set_desired_inferior (1);
+
+  return 0;
 }
 
 /* Event-loop callback for target events.  */
 
-void
+int
 handle_target_event (int err, gdb_client_data client_data)
 {
   if (debug_threads)
@@ -2502,11 +3105,58 @@ handle_target_event (int err, gdb_client_data client_data)
 
   if (last_status.kind != TARGET_WAITKIND_IGNORE)
     {
-      /* Something interesting.  Tell GDB about it.  */
-      push_event (last_ptid, &last_status);
+      int pid = ptid_get_pid (last_ptid);
+      struct process_info *process = find_process_pid (pid);
+      int forward_event = !gdb_connected () || process->gdb_detached;
+
+      if (last_status.kind == TARGET_WAITKIND_EXITED
+         || last_status.kind == TARGET_WAITKIND_SIGNALLED)
+       {
+         mark_breakpoints_out (process);
+         mourn_inferior (process);
+       }
+
+      if (forward_event)
+       {
+         if (!target_running ())
+           {
+             /* The last process exited.  We're done.  */
+             exit (0);
+           }
+
+         if (last_status.kind == TARGET_WAITKIND_STOPPED)
+           {
+             /* A thread stopped with a signal, but gdb isn't
+                connected to handle it.  Pass it down to the
+                inferior, as if it wasn't being traced.  */
+             struct thread_resume resume_info;
+
+             if (debug_threads)
+               fprintf (stderr,
+                        "GDB not connected; forwarding event %d for [%s]\n",
+                        (int) last_status.kind,
+                        target_pid_to_str (last_ptid));
+
+             resume_info.thread = last_ptid;
+             resume_info.kind = resume_continue;
+             resume_info.sig = last_status.value.sig;
+             (*the_target->resume) (&resume_info, 1);
+           }
+         else if (debug_threads)
+           fprintf (stderr, "GDB not connected; ignoring event %d for [%s]\n",
+                    (int) last_status.kind,
+                    target_pid_to_str (last_ptid));
+       }
+      else
+       {
+         /* Something interesting.  Tell GDB about it.  */
+         push_event (last_ptid, &last_status);
+       }
     }
 
   /* Be sure to not change the selected inferior behind GDB's back.
      Important in the non-stop mode asynchronous protocol.  */
   set_desired_inferior (1);
+
+  return 0;
 }
This page took 0.045731 seconds and 4 git commands to generate.