#include "agent.h"
#include "notif.h"
#include "tdesc.h"
+#include "rsp-low.h"
+#include <ctype.h>
#include <unistd.h>
#if HAVE_SIGNAL_H
#include <signal.h>
#endif
+#include "gdb_vecs.h"
#include "gdb_wait.h"
#include "btrace-common.h"
#include "filestuff.h"
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;
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
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");
}
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)
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");
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");
}
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);
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);
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. */
+ is found or CALLBACK results false.
+ Note: This function is itself a callback for find_inferior. */
static int
-visit_actioned_threads (const struct thread_resume *actions,
- size_t num_actions,
- int (*callback) (const struct thread_resume *,
- struct thread_info *))
+visit_actioned_threads (struct inferior_list_entry *entry, void *datap)
{
- struct inferior_list_entry *entry;
+ 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 (entry = all_threads.head; entry != NULL; entry = entry->next)
+ for (i = 0; i < num_actions; i++)
{
- size_t i;
+ const struct thread_resume *action = &actions[i];
- for (i = 0; i < num_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))
{
- 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;
+ struct thread_info *thread = (struct thread_info *) entry;
- if ((*callback) (action, thread))
- return 1;
- }
+ if ((*callback) (action, thread))
+ return 1;
}
}
one with a pending status to report. If so, skip actually
resuming/stopping and report the pending event
immediately. */
- if (visit_actioned_threads (actions, num_actions, handle_pending_status))
+ struct visit_actioned_threads_data data;
+
+ data.actions = actions;
+ data.num_actions = num_actions;
+ data.callback = handle_pending_status;
+ if (find_inferior (&all_threads, visit_actioned_threads, &data) != NULL)
return;
enable_async_io ();
{
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
{
/* 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';
}
char *status_string
= target_waitstatus_to_string (&thread->last_status);
- fprintf (stderr,
- "Reporting thread %s as already stopped with %s\n",
- target_pid_to_str (entry->id),
- status_string);
+ debug_printf ("Reporting thread %s as already stopped with %s\n",
+ target_pid_to_str (entry->id),
+ status_string);
xfree (status_string);
}
/* If we're still out of luck, simply pick the first thread in
the thread list. */
if (thread == NULL)
- thread = all_threads.head;
+ thread = get_first_inferior (&all_threads);
if (thread != NULL)
{
"\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)
}
}
+/* 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. */
{
/* Conditional expression. */
if (debug_threads)
- fprintf (stderr, "Found breakpoint condition.\n");
- add_breakpoint_condition (point_addr, &dataptr);
+ 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;
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
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
{