/* Main code for remote server for GDB.
- Copyright (C) 1989-2013 Free Software Foundation, Inc.
+ Copyright (C) 1989-2014 Free Software Foundation, Inc.
This file is part of GDB.
#include "gdbthread.h"
#include "agent.h"
#include "notif.h"
+#include "tdesc.h"
+#include "rsp-low.h"
-#if HAVE_UNISTD_H
+#include <ctype.h>
#include <unistd.h>
-#endif
#if HAVE_SIGNAL_H
#include <signal.h>
#endif
+#include "gdb_vecs.h"
#include "gdb_wait.h"
+#include "btrace-common.h"
+#include "filestuff.h"
+#include "tracepoint.h"
+#include "dll.h"
+#include "hostio.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 **program_argv, **wrapper_argv;
-/* Enable miscellaneous debugging output. The name is historical - it
- was originally used to debug LinuxThreads support. */
-int debug_threads;
-
/* Enable debugging of h/w breakpoint/watchpoint support. */
int debug_hw_points;
jmp_buf toplevel;
-const char *gdbserver_xmltarget;
-
/* The PID of the originally created or attached inferior. Used to
send signals to the process when GDB sends us an asynchronous interrupt
(user hitting Control-C in the client), and to wait for the child to exit
static int
target_running (void)
{
- return all_threads.head != NULL;
+ return get_first_thread () != NULL;
}
static int
{
int i;
for (i = 0; new_argv[i]; ++i)
- fprintf (stderr, "new_argv[%d] = \"%s\"\n", i, new_argv[i]);
- fflush (stderr);
+ debug_printf ("new_argv[%d] = \"%s\"\n", i, new_argv[i]);
+ debug_flush ();
}
#ifdef SIGTTOU
{
struct thread_resume resume_info;
+ memset (&resume_info, 0, sizeof (resume_info));
resume_info.thread = pid_to_ptid (signal_pid);
resume_info.kind = resume_continue;
resume_info.sig = 0;
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;
static const char *
get_features_xml (const char *annex)
{
- /* gdbserver_xmltarget defines what to return when looking
- for the "target.xml" file. Its contents can either be
- verbatim XML code (prefixed with a '@') or else the name
- of the actual XML file to be used in place of "target.xml".
+ const struct target_desc *desc = current_target_desc ();
+
+ /* `desc->xmltarget' defines what to return when looking for the
+ "target.xml" file. Its contents can either be verbatim XML code
+ (prefixed with a '@') or else the name of the actual XML file to
+ be used in place of "target.xml".
This variable is set up from the auto-generated
init_registers_... routine for the current target. */
- if (gdbserver_xmltarget
- && strcmp (annex, "target.xml") == 0)
+ if (desc->xmltarget != NULL && strcmp (annex, "target.xml") == 0)
{
- if (*gdbserver_xmltarget == '@')
- return gdbserver_xmltarget + 1;
+ if (*desc->xmltarget == '@')
+ return desc->xmltarget + 1;
else
- annex = gdbserver_xmltarget;
+ annex = desc->xmltarget;
}
#ifdef USE_XML
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 (" set debug-format option1[,option2,...]\n");
+ monitor_output (" Add additional information to debugging messages\n");
+ monitor_output (" Options: all, none");
+#ifdef HAVE_GETTIMEOFDAY
+ monitor_output (", timestamp");
+#endif
+ monitor_output ("\n");
monitor_output (" exit\n");
monitor_output (" Quit GDBserver\n");
}
if (traceframe_read_mem (current_traceframe,
memaddr, myaddr, len, &nbytes))
- return EIO;
+ return -1;
/* Data read from trace buffer, we're done. */
if (nbytes > 0)
return nbytes;
return; \
}
+/* Parse options to --debug-format= and "monitor set debug-format".
+ ARG is the text after "--debug-format=" or "monitor set debug-format".
+ IS_MONITOR is non-zero if we're invoked via "monitor set debug-format".
+ This triggers calls to monitor_output.
+ The result is NULL if all options were parsed ok, otherwise an error
+ message which the caller must free.
+
+ N.B. These commands affect all debug format settings, they are not
+ cumulative. If a format is not specified, it is turned off.
+ However, we don't go to extra trouble with things like
+ "monitor set debug-format all,none,timestamp".
+ Instead we just parse them one at a time, in order.
+
+ The syntax for "monitor set debug" we support here is not identical
+ to gdb's "set debug foo on|off" because we also use this function to
+ parse "--debug-format=foo,bar". */
+
+static char *
+parse_debug_format_options (const char *arg, int is_monitor)
+{
+ VEC (char_ptr) *options;
+ int ix;
+ char *option;
+
+ /* First turn all debug format options off. */
+ debug_timestamp = 0;
+
+ /* First remove leading spaces, for "monitor set debug-format". */
+ while (isspace (*arg))
+ ++arg;
+
+ options = delim_string_to_char_ptr_vec (arg, ',');
+
+ for (ix = 0; VEC_iterate (char_ptr, options, ix, option); ++ix)
+ {
+ if (strcmp (option, "all") == 0)
+ {
+ debug_timestamp = 1;
+ if (is_monitor)
+ monitor_output ("All extra debug format options enabled.\n");
+ }
+ else if (strcmp (option, "none") == 0)
+ {
+ debug_timestamp = 0;
+ if (is_monitor)
+ monitor_output ("All extra debug format options disabled.\n");
+ }
+#ifdef HAVE_GETTIMEOFDAY
+ else if (strcmp (option, "timestamp") == 0)
+ {
+ debug_timestamp = 1;
+ if (is_monitor)
+ monitor_output ("Timestamps will be added to debug output.\n");
+ }
+#endif
+ else if (*option == '\0')
+ {
+ /* An empty option, e.g., "--debug-format=foo,,bar", is ignored. */
+ continue;
+ }
+ else
+ {
+ char *msg = xstrprintf ("Unknown debug-format argument: \"%s\"\n",
+ option);
+
+ free_char_ptr_vec (options);
+ return msg;
+ }
+ }
+
+ free_char_ptr_vec (options);
+ return NULL;
+}
+
/* Handle monitor commands not handled by target-specific handlers. */
static void
remote_debug = 0;
monitor_output ("Protocol debug output disabled.\n");
}
+ else if (strncmp (mon, "set debug-format ",
+ sizeof ("set debug-format ") - 1) == 0)
+ {
+ char *error_msg
+ = parse_debug_format_options (mon + sizeof ("set debug-format ") - 1,
+ 1);
+
+ if (error_msg != NULL)
+ {
+ monitor_output (error_msg);
+ monitor_show_help ();
+ write_enn (own_buf);
+ xfree (error_msg);
+ }
+ }
else if (strcmp (mon, "help") == 0)
monitor_show_help ();
else if (strcmp (mon, "exit") == 0)
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 len;
}
+/* Worker routine for handle_qxfer_libraries.
+ Add to the length pointed to by ARG a conservative estimate of the
+ length needed to transmit the file name of INF. */
+
+static void
+accumulate_file_name_length (struct inferior_list_entry *inf, void *arg)
+{
+ struct dll_info *dll = (struct dll_info *) inf;
+ unsigned int *total_len = arg;
+
+ /* Over-estimate the necessary memory. Assume that every character
+ in the library name must be escaped. */
+ *total_len += 128 + 6 * strlen (dll->name);
+}
+
+/* Worker routine for handle_qxfer_libraries.
+ Emit the XML to describe the library in INF. */
+
+static void
+emit_dll_description (struct inferior_list_entry *inf, void *arg)
+{
+ struct dll_info *dll = (struct dll_info *) inf;
+ char **p_ptr = arg;
+ char *p = *p_ptr;
+ char *name;
+
+ strcpy (p, " <library name=\"");
+ p = p + strlen (p);
+ name = xml_escape_text (dll->name);
+ strcpy (p, name);
+ free (name);
+ p = p + strlen (p);
+ strcpy (p, "\"><segment address=\"");
+ p = p + strlen (p);
+ sprintf (p, "0x%lx", (long) dll->base_addr);
+ p = p + strlen (p);
+ strcpy (p, "\"/></library>\n");
+ p = p + strlen (p);
+
+ *p_ptr = p;
+}
+
/* Handle qXfer:libraries:read. */
static int
{
unsigned int total_len;
char *document, *p;
- struct inferior_list_entry *dll_ptr;
if (writebuf != NULL)
return -2;
if (annex[0] != '\0' || !target_running ())
return -1;
- /* Over-estimate the necessary memory. Assume that every character
- in the library name must be escaped. */
total_len = 64;
- for (dll_ptr = all_dlls.head; dll_ptr != NULL; dll_ptr = dll_ptr->next)
- total_len += 128 + 6 * strlen (((struct dll_info *) dll_ptr)->name);
+ for_each_inferior_with_data (&all_dlls, accumulate_file_name_length,
+ &total_len);
document = malloc (total_len);
if (document == NULL)
strcpy (document, "<library-list>\n");
p = document + strlen (document);
- for (dll_ptr = all_dlls.head; dll_ptr != NULL; dll_ptr = dll_ptr->next)
- {
- struct dll_info *dll = (struct dll_info *) dll_ptr;
- char *name;
-
- strcpy (p, " <library name=\"");
- p = p + strlen (p);
- name = xml_escape_text (dll->name);
- strcpy (p, name);
- free (name);
- p = p + strlen (p);
- strcpy (p, "\"><segment address=\"");
- p = p + strlen (p);
- sprintf (p, "0x%lx", (long) dll->base_addr);
- p = p + strlen (p);
- strcpy (p, "\"/></library>\n");
- p = p + strlen (p);
- }
+ for_each_inferior_with_data (&all_dlls, emit_dll_description, &p);
strcpy (p, "</library-list>\n");
if (writebuf != NULL)
return -2;
- if (annex[0] != '\0' || !target_running ()
- || the_target->qxfer_libraries_svr4 == NULL)
+ if (!target_running () || the_target->qxfer_libraries_svr4 == NULL)
return -1;
return the_target->qxfer_libraries_svr4 (annex, readbuf, writebuf, offset, len);
return nbytes;
}
-/* Helper for handle_qxfer_threads. */
+/* Helper for handle_qxfer_threads_proper.
+ Emit the XML to describe the thread of INF. */
static void
-handle_qxfer_threads_proper (struct buffer *buffer)
+handle_qxfer_threads_worker (struct inferior_list_entry *inf, void *arg)
{
- struct inferior_list_entry *thread;
+ struct thread_info *thread = (struct thread_info *) inf;
+ struct buffer *buffer = arg;
+ ptid_t ptid = thread_to_gdb_id (thread);
+ char ptid_s[100];
+ int core = target_core_of_thread (ptid);
+ char core_s[21];
- buffer_grow_str (buffer, "<threads>\n");
+ write_ptid (ptid_s, ptid);
- for (thread = all_threads.head; thread; thread = thread->next)
+ if (core != -1)
{
- ptid_t ptid = thread_to_gdb_id ((struct thread_info *)thread);
- char ptid_s[100];
- int core = target_core_of_thread (ptid);
- char core_s[21];
+ 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);
+ }
+}
- write_ptid (ptid_s, ptid);
+/* Helper for handle_qxfer_threads. */
- 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);
- }
- }
+static void
+handle_qxfer_threads_proper (struct buffer *buffer)
+{
+ buffer_grow_str (buffer, "<threads>\n");
+
+ for_each_inferior_with_data (&all_threads, handle_qxfer_threads_worker,
+ buffer);
buffer_grow_str0 (buffer, "</threads>\n");
}
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, result;
+
+ 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 if (strcmp (annex, "delta") == 0)
+ type = BTRACE_READ_DELTA;
+ else
+ {
+ strcpy (own_buf, "E.Bad annex.");
+ return -3;
+ }
+
+ if (offset == 0)
+ {
+ buffer_free (&cache);
+
+ result = target_read_btrace (thread->btrace, &cache, type);
+ if (result != 0)
+ {
+ memcpy (own_buf, cache.buffer, cache.used_size);
+ return -3;
+ }
+ }
+ 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
gdb_id = general_thread;
else
{
- thread_ptr = all_threads.head;
+ thread_ptr = get_first_inferior (&all_threads);
gdb_id = thread_to_gdb_id ((struct thread_info *)thread_ptr);
}
ptid_t gdb_id;
require_running (own_buf);
- thread_ptr = all_threads.head;
+ thread_ptr = get_first_inferior (&all_threads);
*own_buf++ = 'm';
gdb_id = thread_to_gdb_id ((struct thread_info *)thread_ptr);
PBUFSIZ - 1);
if (the_target->qxfer_libraries_svr4 != NULL)
- strcat (own_buf, ";qXfer:libraries-svr4:read+");
+ strcat (own_buf, ";qXfer:libraries-svr4:read+"
+ ";augmented-libraries-svr4-read+");
else
{
/* We do not have any hook to indicate whether the non-SVR4 target
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;
}
return;
}
- if ((len % 2) != 0 || unhexify (mon, own_buf + 6, len / 2) != len / 2)
+ if ((len % 2) != 0
+ || hex2bin (own_buf + 6, (gdb_byte *) mon, len / 2) != len / 2)
{
write_enn (own_buf);
free (mon);
{
/* 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);
}
static void gdb_wants_all_threads_stopped (void);
+static void resume (struct thread_resume *actions, size_t n);
+
+/* The callback that is passed to visit_actioned_threads. */
+typedef int (visit_actioned_threads_callback_ftype)
+ (const struct thread_resume *, struct thread_info *);
+
+/* Struct to pass data to visit_actioned_threads. */
+
+struct visit_actioned_threads_data
+{
+ const struct thread_resume *actions;
+ size_t num_actions;
+ visit_actioned_threads_callback_ftype *callback;
+};
+
+/* Call CALLBACK for any thread to which ACTIONS applies to. Returns
+ true if CALLBACK returns true. Returns false if no matching thread
+ is found or CALLBACK results false.
+ Note: This function is itself a callback for find_inferior. */
+
+static int
+visit_actioned_threads (struct inferior_list_entry *entry, void *datap)
+{
+ struct visit_actioned_threads_data *data = datap;
+ const struct thread_resume *actions = data->actions;
+ size_t num_actions = data->num_actions;
+ visit_actioned_threads_callback_ftype *callback = data->callback;
+ size_t i;
+
+ for (i = 0; i < num_actions; i++)
+ {
+ const struct thread_resume *action = &actions[i];
+
+ if (ptid_equal (action->thread, minus_one_ptid)
+ || ptid_equal (action->thread, entry->id)
+ || ((ptid_get_pid (action->thread)
+ == ptid_get_pid (entry->id))
+ && ptid_get_lwp (action->thread) == -1))
+ {
+ struct thread_info *thread = (struct thread_info *) entry;
+
+ if ((*callback) (action, thread))
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/* Callback for visit_actioned_threads. If the thread has a pending
+ status to report, report it now. */
+
+static int
+handle_pending_status (const struct thread_resume *resumption,
+ struct thread_info *thread)
+{
+ if (thread->status_pending_p)
+ {
+ thread->status_pending_p = 0;
+
+ last_status = thread->last_status;
+ last_ptid = thread->entry.id;
+ prepare_resume_reply (own_buf, last_ptid, &last_status);
+ return 1;
+ }
+ return 0;
+}
/* Parse vCont packets. */
void
{
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')
+ {
+ ULONGEST addr;
+
+ p = unpack_varlen_hex (p + 1, &addr);
+ resume_info[i].step_range_start = addr;
+
+ if (*p != ',')
+ goto err;
+
+ p = unpack_varlen_hex (p + 1, &addr);
+ resume_info[i].step_range_end = addr;
+ }
else
{
- resume_info[i].sig = 0;
p = p + 1;
}
cont_thread = minus_one_ptid;
set_desired_inferior (0);
+ resume (resume_info, n);
+ free (resume_info);
+ return;
+
+err:
+ write_enn (own_buf);
+ free (resume_info);
+ return;
+}
+
+/* Resume target with ACTIONS, an array of NUM_ACTIONS elements. */
+
+static void
+resume (struct thread_resume *actions, size_t num_actions)
+{
if (!non_stop)
- enable_async_io ();
+ {
+ /* Check if among the threads that GDB wants actioned, there's
+ one with a pending status to report. If so, skip actually
+ resuming/stopping and report the pending event
+ immediately. */
+ struct visit_actioned_threads_data data;
- (*the_target->resume) (resume_info, n);
+ data.actions = actions;
+ data.num_actions = num_actions;
+ data.callback = handle_pending_status;
+ if (find_inferior (&all_threads, visit_actioned_threads, &data) != NULL)
+ return;
- free (resume_info);
+ enable_async_io ();
+ }
+
+ (*the_target->resume) (actions, num_actions);
if (non_stop)
write_ok (own_buf);
{
last_ptid = mywait (minus_one_ptid, &last_status, 0, 1);
+ if (last_status.kind == TARGET_WAITKIND_NO_RESUMED)
+ {
+ /* No proper RSP support for this yet. At least return
+ error. */
+ sprintf (own_buf, "E.No unwaited-for children left.");
+ disable_async_io ();
+ return;
+ }
+
if (last_status.kind != TARGET_WAITKIND_EXITED
- && last_status.kind != TARGET_WAITKIND_SIGNALLED)
+ && last_status.kind != TARGET_WAITKIND_SIGNALLED
+ && last_status.kind != TARGET_WAITKIND_NO_RESUMED)
current_inferior->last_status = last_status;
/* From the client's perspective, all-stop mode always stops all
|| last_status.kind == TARGET_WAITKIND_SIGNALLED)
mourn_inferior (find_process_pid (ptid_get_pid (last_ptid)));
}
- return;
-
-err:
- write_enn (own_buf);
- free (resume_info);
- return;
}
/* Attach to a new program. Return 1 if successful, 0 if failure. */
{
/* FIXME: Fail request if out of memory instead of dying. */
new_argv[i] = xmalloc (1 + (next_p - p) / 2);
- unhexify (new_argv[i], p, (next_p - p) / 2);
+ hex2bin (p, (gdb_byte *) new_argv[i], (next_p - p) / 2);
new_argv[i][(next_p - p) / 2] = '\0';
}
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;
}
}
n++;
}
- if (!non_stop)
- enable_async_io ();
-
- (*the_target->resume) (resume_info, n);
-
- if (non_stop)
- write_ok (own_buf);
- else
- {
- last_ptid = mywait (minus_one_ptid, &last_status, 0, 1);
-
- if (last_status.kind != TARGET_WAITKIND_EXITED
- && last_status.kind != TARGET_WAITKIND_SIGNALLED)
- {
- current_inferior->last_resume_kind = resume_stop;
- current_inferior->last_status = last_status;
- }
-
- prepare_resume_reply (own_buf, last_ptid, &last_status);
- disable_async_io ();
-
- if (last_status.kind == TARGET_WAITKIND_EXITED
- || last_status.kind == TARGET_WAITKIND_SIGNALLED)
- mourn_inferior (find_process_pid (ptid_get_pid (last_ptid)));
- }
+ resume (resume_info, n);
}
/* Callback for for_each_inferior. Make a new stop reply for each
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));
+ {
+ char *status_string
+ = target_waitstatus_to_string (&thread->last_status);
+
+ debug_printf ("Reporting thread %s as already stopped with %s\n",
+ target_pid_to_str (entry->id),
+ status_string);
+
+ xfree (status_string);
+ }
gdb_assert (thread->last_status.kind != TARGET_WAITKIND_IGNORE);
process->gdb_detached = 0;
}
+/* Callback for for_each_inferior. Clear the thread's pending status
+ flag. */
+
+static void
+clear_pending_status_callback (struct inferior_list_entry *entry)
+{
+ struct thread_info *thread = (struct thread_info *) entry;
+
+ thread->status_pending_p = 0;
+}
+
+/* Callback for for_each_inferior. If the thread is stopped with an
+ interesting event, mark it as having a pending event. */
+
+static void
+set_pending_status_callback (struct inferior_list_entry *entry)
+{
+ struct thread_info *thread = (struct thread_info *) entry;
+
+ if (thread->last_status.kind != TARGET_WAITKIND_STOPPED
+ || (thread->last_status.value.sig != GDB_SIGNAL_0
+ /* A breakpoint, watchpoint or finished step from a previous
+ GDB run isn't considered interesting for a new GDB run.
+ If we left those pending, the new GDB could consider them
+ random SIGTRAPs. This leaves out real async traps. We'd
+ have to peek into the (target-specific) siginfo to
+ distinguish those. */
+ && thread->last_status.value.sig != GDB_SIGNAL_TRAP))
+ thread->status_pending_p = 1;
+}
+
+/* Callback for find_inferior. Return true if ENTRY (a thread) has a
+ pending status to report to GDB. */
+
+static int
+find_status_pending_thread_callback (struct inferior_list_entry *entry, void *data)
+{
+ struct thread_info *thread = (struct thread_info *) entry;
+
+ return thread->status_pending_p;
+}
+
/* Status handler for the '?' packet. */
static void
if (non_stop)
{
- 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
}
else
{
+ struct inferior_list_entry *thread = NULL;
+
pause_all (0);
stabilize_threads ();
gdb_wants_all_threads_stopped ();
- if (all_threads.head)
- {
- struct target_waitstatus status;
+ /* We can only report one status, but we might be coming out of
+ non-stop -- if more than one thread is stopped with
+ interesting events, leave events for the threads we're not
+ reporting now pending. They'll be reported the next time the
+ threads are resumed. Start by marking all interesting events
+ as pending. */
+ for_each_inferior (&all_threads, set_pending_status_callback);
+
+ /* Prefer the last thread that reported an event to GDB (even if
+ that was a GDB_SIGNAL_TRAP). */
+ if (last_status.kind != TARGET_WAITKIND_IGNORE
+ && last_status.kind != TARGET_WAITKIND_EXITED
+ && last_status.kind != TARGET_WAITKIND_SIGNALLED)
+ thread = find_inferior_id (&all_threads, last_ptid);
+
+ /* If the last event thread is not found for some reason, look
+ for some other thread that might have an event to report. */
+ if (thread == NULL)
+ thread = find_inferior (&all_threads,
+ find_status_pending_thread_callback, NULL);
+
+ /* If we're still out of luck, simply pick the first thread in
+ the thread list. */
+ if (thread == NULL)
+ thread = get_first_inferior (&all_threads);
+
+ if (thread != NULL)
+ {
+ struct thread_info *tp = (struct thread_info *) thread;
+
+ /* We're reporting this event, so it's no longer
+ pending. */
+ tp->status_pending_p = 0;
+
+ /* GDB assumes the current thread is the thread we're
+ reporting the status for. */
+ general_thread = thread->id;
+ set_desired_inferior (1);
- status.kind = TARGET_WAITKIND_STOPPED;
- status.value.sig = GDB_SIGNAL_TRAP;
- prepare_resume_reply (own_buf,
- all_threads.head->id, &status);
+ gdb_assert (tp->last_status.kind != TARGET_WAITKIND_IGNORE);
+ prepare_resume_reply (own_buf, tp->entry.id, &tp->last_status);
}
else
strcpy (own_buf, "W00");
gdbserver_version (void)
{
printf ("GNU gdbserver %s%s\n"
- "Copyright (C) 2013 Free Software Foundation, Inc.\n"
+ "Copyright (C) 2014 Free Software Foundation, Inc.\n"
"gdbserver is free software, covered by the "
"GNU General Public License.\n"
"This gdbserver was configured as \"%s\"\n",
"\n"
"Options:\n"
" --debug Enable general debugging output.\n"
+ " --debug-format=opt1[,opt2,...]\n"
+ " Specify extra content in debugging output.\n"
+ " Options:\n"
+ " all\n"
+ " none\n"
+#ifdef HAVE_GETTIMEOFDAY
+ " timestamp\n"
+#endif
" --remote-debug Enable remote protocol debugging output.\n"
" --version Display version information and exit.\n"
" --wrapper WRAPPER -- Run WRAPPER to start new programs.\n"
kill_inferior_callback (struct inferior_list_entry *entry)
{
struct process_info *process = (struct process_info *) entry;
- int pid = ptid_get_pid (process->head.id);
+ int pid = ptid_get_pid (process->entry.id);
kill_inferior (pid);
discard_queued_stop_replies (pid);
detach_or_kill_inferior_callback (struct inferior_list_entry *entry)
{
struct process_info *process = (struct process_info *) entry;
- int pid = ptid_get_pid (process->head.id);
+ int pid = ptid_get_pid (process->entry.id);
if (process->attached)
detach_inferior (pid);
if (! process->attached)
{
- int pid = ptid_get_pid (process->head.id);
+ int pid = ptid_get_pid (process->entry.id);
fprintf (stderr, " %d", pid);
}
}
if (process->attached)
{
- int pid = ptid_get_pid (process->head.id);
+ int pid = ptid_get_pid (process->entry.id);
fprintf (stderr, " %d", pid);
}
}
}
else if (strcmp (*next_arg, "--debug") == 0)
debug_threads = 1;
+ else if (strncmp (*next_arg,
+ "--debug-format=",
+ sizeof ("--debug-format=") - 1) == 0)
+ {
+ char *error_msg
+ = parse_debug_format_options ((*next_arg)
+ + sizeof ("--debug-format=") - 1, 0);
+
+ if (error_msg != NULL)
+ {
+ fprintf (stderr, "%s", error_msg);
+ exit (1);
+ }
+ }
else if (strcmp (*next_arg, "--remote-debug") == 0)
remote_debug = 1;
else if (strcmp (*next_arg, "--disable-packet") == 0)
exit (1);
}
+ /* Remember stdio descriptors. LISTEN_DESC must not be listed, it will be
+ opened by remote_prepare. */
+ notice_open_fds ();
+
/* We need to know whether the remote connection is stdio before
starting the inferior. Inferiors created in this scenario have
stdin,stdout redirected. So do this here before we call
"Remote side has terminated connection. "
"GDBserver will reopen the connection.\n");
+ /* Get rid of any pending statuses. An eventual reconnection
+ (by the same GDB instance or another) will refresh all its
+ state from scratch. */
+ discard_queued_stop_replies (-1);
+ for_each_inferior (&all_threads, clear_pending_status_callback);
+
if (tracing)
{
if (disconnected_tracing)
}
}
+/* Skip PACKET until the next semi-colon (or end of string). */
+
+static void
+skip_to_semicolon (char **packet)
+{
+ while (**packet != '\0' && **packet != ';')
+ (*packet)++;
+}
+
/* 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. */
if (*dataptr == 'X')
{
/* Conditional expression. */
- fprintf (stderr, "Found breakpoint condition.\n");
- add_breakpoint_condition (point_addr, &dataptr);
+ if (debug_threads)
+ debug_printf ("Found breakpoint condition.\n");
+ if (!add_breakpoint_condition (point_addr, &dataptr))
+ skip_to_semicolon (&dataptr);
}
else if (strncmp (dataptr, "cmds:", strlen ("cmds:")) == 0)
{
dataptr += strlen ("cmds:");
if (debug_threads)
- fprintf (stderr, "Found breakpoint commands %s.\n", dataptr);
+ debug_printf ("Found breakpoint commands %s.\n", dataptr);
persist = (*dataptr == '1');
dataptr += 2;
- add_breakpoint_commands (point_addr, &dataptr, persist);
+ if (add_breakpoint_commands (point_addr, &dataptr, persist))
+ skip_to_semicolon (&dataptr);
}
else
{
fprintf (stderr, "Unknown token %c, ignoring.\n",
*dataptr);
/* Skip tokens until we find one that we recognize. */
- while (*dataptr && *dataptr != ';')
- dataptr++;
+ skip_to_semicolon (&dataptr);
}
}
*packet = dataptr;
if (!non_stop)
{
if (debug_threads)
- fprintf (stderr, "Forcing non-stop mode\n");
+ debug_printf ("Forcing non-stop mode\n");
non_stop = 1;
start_non_stop (1);
break;
}
- thread_id = ((struct inferior_list_entry *)thread)->id;
+ thread_id = thread->entry.id;
}
else
{
(struct thread_info *) find_inferior_id (&all_threads,
general_thread);
if (thread == NULL)
- thread_id = all_threads.head->id;
+ {
+ thread = get_first_thread ();
+ thread_id = thread->entry.id;
+ }
}
general_thread = thread_id;
require_running (own_buf);
if (current_traceframe >= 0)
{
- struct regcache *regcache = new_register_cache ();
+ struct regcache *regcache
+ = new_register_cache (current_target_desc ());
if (fetch_traceframe_registers (current_traceframe,
regcache, -1) == 0)
if (res < 0)
write_enn (own_buf);
else
- convert_int_to_ascii (mem_buf, own_buf, res);
+ bin2hex (mem_buf, own_buf, res);
break;
case 'M':
require_running (own_buf);
break;
case 'C':
require_running (own_buf);
- convert_ascii_to_int (own_buf + 1, &sig, 1);
+ hex2bin (own_buf + 1, &sig, 1);
if (gdb_signal_to_host_p (sig))
signal = gdb_signal_to_host (sig);
else
break;
case 'S':
require_running (own_buf);
- convert_ascii_to_int (own_buf + 1, &sig, 1);
+ hex2bin (own_buf + 1, &sig, 1);
if (gdb_signal_to_host_p (sig))
signal = gdb_signal_to_host (sig);
else
/* 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;
the whole vStopped list (until it gets an OK). */
if (QUEUE_is_empty (notif_event_p, notif_stop.queue))
{
- fprintf (stderr, "GDBserver exiting\n");
+ /* Be transparent when GDB is connected through stdio -- no
+ need to spam GDB's console. */
+ if (!remote_connection_is_stdio ())
+ fprintf (stderr, "GDBserver exiting\n");
remote_close ();
exit (0);
}
handle_serial_event (int err, gdb_client_data client_data)
{
if (debug_threads)
- fprintf (stderr, "handling possible serial event\n");
+ debug_printf ("handling possible serial event\n");
/* Really handle it. */
if (process_serial_event () < 0)
handle_target_event (int err, gdb_client_data client_data)
{
if (debug_threads)
- fprintf (stderr, "handling possible target event\n");
+ debug_printf ("handling possible target event\n");
last_ptid = mywait (minus_one_ptid, &last_status,
TARGET_WNOHANG, 1);
- if (last_status.kind != TARGET_WAITKIND_IGNORE)
+ if (last_status.kind == TARGET_WAITKIND_NO_RESUMED)
+ {
+ /* No RSP support for this yet. */
+ }
+ else if (last_status.kind != TARGET_WAITKIND_IGNORE)
{
int pid = ptid_get_pid (last_ptid);
struct process_info *process = find_process_pid (pid);
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));
+ debug_printf ("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;
(*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));
+ debug_printf ("GDB not connected; ignoring event %d for [%s]\n",
+ (int) last_status.kind,
+ target_pid_to_str (last_ptid));
}
else
{