Add NetBSD/i386 gdbserver support
[binutils-gdb.git] / gdbserver / server.cc
index 43962adc86c456c72a7a57aa8afe68cb8e5ff4ce..d45154d1f54777d25b4f53a2f09e35ef1329dc51 100644 (file)
@@ -47,6 +47,8 @@
 
 #include "gdbsupport/selftest.h"
 #include "gdbsupport/scope-exit.h"
+#include "gdbsupport/gdb_select.h"
+#include "gdbsupport/scoped_restore.h"
 
 #define require_running_or_return(BUF)         \
   if (!target_running ())                      \
@@ -82,6 +84,10 @@ bool run_once;
 /* Whether to report TARGET_WAITKIND_NO_RESUMED events.  */
 static bool report_no_resumed;
 
+/* The event loop checks this to decide whether to continue accepting
+   events.  */
+static bool keep_processing_events = true;
+
 bool non_stop;
 
 static struct {
@@ -1098,7 +1104,7 @@ handle_search_memory_1 (CORE_ADDR start_addr, CORE_ADDR search_space_len,
                        : chunk_size);
 
          if (gdb_read_memory (read_addr, search_buf + keep_len,
-                              nr_to_read) != search_buf_size)
+                              nr_to_read) != nr_to_read)
            {
              warning ("Unable to access %ld bytes of target memory "
                       "at 0x%lx, halting search.",
@@ -1673,19 +1679,54 @@ handle_qxfer_threads_worker (thread_info *thread, struct buffer *buffer)
   buffer_xml_printf (buffer, "/>\n");
 }
 
-/* Helper for handle_qxfer_threads.  */
+/* Helper for handle_qxfer_threads.  Return true on success, false
+   otherwise.  */
 
-static void
+static bool
 handle_qxfer_threads_proper (struct buffer *buffer)
 {
+  client_state &cs = get_client_state ();
+
+  scoped_restore save_current_thread
+    = make_scoped_restore (&current_thread);
+  scoped_restore save_current_general_thread
+    = make_scoped_restore (&cs.general_thread);
+
   buffer_grow_str (buffer, "<threads>\n");
 
-  for_each_thread ([&] (thread_info *thread)
+  process_info *error_proc = find_process ([&] (process_info *process)
     {
-      handle_qxfer_threads_worker (thread, buffer);
+      /* The target may need to access memory and registers (e.g. via
+        libthread_db) to fetch thread properties.  Prepare for memory
+        access here, so that we potentially pause threads just once
+        for all accesses.  Note that even if someday we stop needing
+        to pause threads to access memory, we will need to be able to
+        access registers, or other ptrace accesses like
+        PTRACE_GET_THREAD_AREA.  */
+
+      /* Need to switch to each process in turn, because
+        prepare_to_access_memory prepares for an access in the
+        current process pointed to by general_thread.  */
+      switch_to_process (process);
+      cs.general_thread = current_thread->id;
+
+      int res = prepare_to_access_memory ();
+      if (res == 0)
+       {
+         for_each_thread (process->pid, [&] (thread_info *thread)
+           {
+             handle_qxfer_threads_worker (thread, buffer);
+           });
+
+         done_accessing_memory ();
+         return false;
+       }
+      else
+       return true;
     });
 
   buffer_grow_str0 (buffer, "</threads>\n");
+  return error_proc == nullptr;
 }
 
 /* Handle qXfer:threads:read.  */
@@ -1714,11 +1755,14 @@ handle_qxfer_threads (const char *annex,
 
       buffer_init (&buffer);
 
-      handle_qxfer_threads_proper (&buffer);
+      bool res = handle_qxfer_threads_proper (&buffer);
 
       result = buffer_finish (&buffer);
       result_length = strlen (result);
       buffer_free (&buffer);
+
+      if (!res)
+       return -1;
     }
 
   if (offset >= result_length)
@@ -2264,10 +2308,8 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
         ';'.  */
       if (*p == ':')
        {
-         char **qsupported = NULL;
-         int count = 0;
-         int unknown = 0;
-         int i;
+         std::vector<std::string> qsupported;
+         std::vector<const char *> unknowns;
 
          /* Two passes, to avoid nested strtok calls in
             target_process_qsupported.  */
@@ -2275,28 +2317,23 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
          for (p = strtok_r (p + 1, ";", &saveptr);
               p != NULL;
               p = strtok_r (NULL, ";", &saveptr))
-           {
-             count++;
-             qsupported = XRESIZEVEC (char *, qsupported, count);
-             qsupported[count - 1] = xstrdup (p);
-           }
+           qsupported.emplace_back (p);
 
-         for (i = 0; i < count; i++)
+         for (const std::string &feature : qsupported)
            {
-             p = qsupported[i];
-             if (strcmp (p, "multiprocess+") == 0)
+             if (feature == "multiprocess+")
                {
                  /* GDB supports and wants multi-process support if
                     possible.  */
                  if (target_supports_multi_process ())
                    cs.multi_process = 1;
                }
-             else if (strcmp (p, "qRelocInsn+") == 0)
+             else if (feature == "qRelocInsn+")
                {
                  /* GDB supports relocate instruction requests.  */
                  gdb_supports_qRelocInsn = 1;
                }
-             else if (strcmp (p, "swbreak+") == 0)
+             else if (feature == "swbreak+")
                {
                  /* GDB wants us to report whether a trap is caused
                     by a software breakpoint and for us to handle PC
@@ -2304,36 +2341,36 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
                  if (target_supports_stopped_by_sw_breakpoint ())
                    cs.swbreak_feature = 1;
                }
-             else if (strcmp (p, "hwbreak+") == 0)
+             else if (feature == "hwbreak+")
                {
                  /* GDB wants us to report whether a trap is caused
                     by a hardware breakpoint.  */
                  if (target_supports_stopped_by_hw_breakpoint ())
                    cs.hwbreak_feature = 1;
                }
-             else if (strcmp (p, "fork-events+") == 0)
+             else if (feature == "fork-events+")
                {
                  /* GDB supports and wants fork events if possible.  */
                  if (target_supports_fork_events ())
                    cs.report_fork_events = 1;
                }
-             else if (strcmp (p, "vfork-events+") == 0)
+             else if (feature == "vfork-events+")
                {
                  /* GDB supports and wants vfork events if possible.  */
                  if (target_supports_vfork_events ())
                    cs.report_vfork_events = 1;
                }
-             else if (strcmp (p, "exec-events+") == 0)
+             else if (feature == "exec-events+")
                {
                  /* GDB supports and wants exec events if possible.  */
                  if (target_supports_exec_events ())
                    cs.report_exec_events = 1;
                }
-             else if (strcmp (p, "vContSupported+") == 0)
+             else if (feature == "vContSupported+")
                cs.vCont_supported = 1;
-             else if (strcmp (p, "QThreadEvents+") == 0)
+             else if (feature == "QThreadEvents+")
                ;
-             else if (strcmp (p, "no-resumed+") == 0)
+             else if (feature == "no-resumed+")
                {
                  /* GDB supports and wants TARGET_WAITKIND_NO_RESUMED
                     events.  */
@@ -2342,19 +2379,13 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
              else
                {
                  /* Move the unknown features all together.  */
-                 qsupported[i] = NULL;
-                 qsupported[unknown] = p;
-                 unknown++;
+                 unknowns.push_back (feature.c_str ());
                }
            }
 
          /* Give the target backend a chance to process the unknown
             features.  */
-         target_process_qsupported (qsupported, unknown);
-
-         for (i = 0; i < count; i++)
-           free (qsupported[i]);
-         free (qsupported);
+         target_process_qsupported (unknowns);
        }
 
       sprintf (own_buf,
@@ -2952,7 +2983,7 @@ handle_v_run (char *own_buf)
       else if (p == next_p)
        {
          /* Empty argument.  */
-         new_argv.push_back (xstrdup ("''"));
+         new_argv.push_back (xstrdup (""));
        }
       else
        {
@@ -3010,7 +3041,6 @@ handle_v_run (char *own_buf)
       if (*next_p)
        next_p++;
     }
-  new_argv.push_back (NULL);
 
   if (new_program_name == NULL)
     {
@@ -3462,6 +3492,32 @@ gdbserver_show_disableable (FILE *stream)
           "  threads     \tAll of the above\n");
 }
 
+/* Start up the event loop.  This is the entry point to the event
+   loop.  */
+
+static void
+start_event_loop ()
+{
+  /* Loop until there is nothing to do.  This is the entry point to
+     the event loop engine.  If nothing is ready at this time, wait
+     for something to happen (via wait_for_event), then process it.
+     Return when there are no longer event sources to wait for.  */
+
+  keep_processing_events = true;
+  while (keep_processing_events)
+    {
+      /* Any events already waiting in the queue?  */
+      int res = gdb_do_one_event ();
+
+      /* Was there an error?  */
+      if (res == -1)
+       break;
+    }
+
+  /* We are done with the event loop.  There are no more event sources
+     to listen to.  So we exit gdbserver.  */
+}
+
 static void
 kill_inferior_callback (process_info *process)
 {
@@ -3554,7 +3610,7 @@ captured_main (int argc, char *argv[])
   int was_running;
   bool selftest = false;
 #if GDB_SELF_TEST
-  const char *selftest_filter = NULL;
+  std::vector<const char *> selftest_filters;
 #endif
 
   current_directory = getcwd (NULL, 0);
@@ -3691,8 +3747,16 @@ captured_main (int argc, char *argv[])
       else if (startswith (*next_arg, "--selftest="))
        {
          selftest = true;
+
 #if GDB_SELF_TEST
-         selftest_filter = *next_arg + strlen ("--selftest=");
+         const char *filter = *next_arg + strlen ("--selftest=");
+         if (*filter == '\0')
+           {
+             fprintf (stderr, _("Error: selftest filter is empty.\n"));
+             exit (1);
+           }
+
+         selftest_filters.push_back (filter);
 #endif
        }
       else
@@ -3761,7 +3825,6 @@ captured_main (int argc, char *argv[])
   initialize_async_io ();
   initialize_low ();
   have_job_control ();
-  initialize_event_loop ();
   if (target_supports_tracepoints ())
     initialize_tracepoint ();
 
@@ -3770,7 +3833,7 @@ captured_main (int argc, char *argv[])
   if (selftest)
     {
 #if GDB_SELF_TEST
-      selftests::run_tests (selftest_filter);
+      selftests::run_tests (selftest_filters);
 #else
       printf (_("Selftests have been disabled for this build.\n"));
 #endif
@@ -3785,7 +3848,6 @@ captured_main (int argc, char *argv[])
       program_path.set (make_unique_xstrdup (next_arg[0]));
       for (i = 1; i < n; i++)
        program_args.push_back (xstrdup (next_arg[i]));
-      program_args.push_back (NULL);
 
       /* Wait till we are at first instruction in program.  */
       target_create_inferior (program_path.get (), program_args);
@@ -4364,7 +4426,7 @@ process_serial_event (void)
 
 /* Event-loop callback for serial events.  */
 
-int
+void
 handle_serial_event (int err, gdb_client_data client_data)
 {
   if (debug_threads)
@@ -4372,13 +4434,14 @@ handle_serial_event (int err, gdb_client_data client_data)
 
   /* Really handle it.  */
   if (process_serial_event () < 0)
-    return -1;
+    {
+      keep_processing_events = false;
+      return;
+    }
 
   /* Be sure to not change the selected thread behind GDB's back.
      Important in the non-stop mode asynchronous protocol.  */
   set_desired_thread ();
-
-  return 0;
 }
 
 /* Push a stop notification on the notification queue.  */
@@ -4396,7 +4459,7 @@ push_stop_notification (ptid_t ptid, struct target_waitstatus *status)
 
 /* Event-loop callback for target events.  */
 
-int
+void
 handle_target_event (int err, gdb_client_data client_data)
 {
   client_state &cs = get_client_state ();
@@ -4473,10 +4536,42 @@ 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 ();
+}
+
+/* See gdbsupport/event-loop.h.  */
+
+int
+invoke_async_signal_handlers ()
+{
+  return 0;
+}
 
+/* See gdbsupport/event-loop.h.  */
+
+int
+check_async_event_handlers ()
+{
   return 0;
 }
 
+/* See gdbsupport/errors.h  */
+
+void
+flush_streams ()
+{
+  fflush (stdout);
+  fflush (stderr);
+}
+
+/* See gdbsupport/gdb_select.h.  */
+
+int
+gdb_select (int n, fd_set *readfds, fd_set *writefds,
+           fd_set *exceptfds, struct timeval *timeout)
+{
+  return select (n, readfds, writefds, exceptfds, timeout);
+}
+
 #if GDB_SELF_TEST
 namespace selftests
 {