Index: gdb/gdbserver/server.c |
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c |
index c75a58ef151887515154123a297646eb7933ba0a..4e15b3c9fa977dd6551ce85a6b8418a7801c9d68 100644 |
--- a/gdb/gdbserver/server.c |
+++ b/gdb/gdbserver/server.c |
@@ -18,6 +18,8 @@ |
along with this program. If not, see <http://www.gnu.org/licenses/>. */ |
#include "server.h" |
+#include "gdbthread.h" |
+#include "agent.h" |
#if HAVE_UNISTD_H |
#include <unistd.h> |
@@ -29,7 +31,19 @@ |
#include <sys/wait.h> |
#endif |
+/* The thread set with an `Hc' packet. `Hc' is deprecated in favor of |
+ `vCont'. Note the multi-process extensions made `vCont' a |
+ requirement, so `Hc pPID.TID' is pretty much undefined. So |
+ CONT_THREAD can be null_ptid for no `Hc' thread, minus_one_ptid for |
+ resuming all threads of the process (again, `Hc' isn't used for |
+ multi-process), or a specific thread ptid_t. |
+ |
+ We also set this when handling a single-thread `vCont' resume, as |
+ some places in the backends check it to know when (and for which |
+ thread) single-thread scheduler-locking is in effect. */ |
ptid_t cont_thread; |
+ |
+/* The thread set with an `Hg' packet. */ |
ptid_t general_thread; |
int server_waiting; |
@@ -57,7 +71,9 @@ int debug_threads; |
/* Enable debugging of h/w breakpoint/watchpoint support. */ |
int debug_hw_points; |
-int pass_signals[TARGET_SIGNAL_LAST]; |
+int pass_signals[GDB_SIGNAL_LAST]; |
+int program_signals[GDB_SIGNAL_LAST]; |
+int program_signals_p; |
jmp_buf toplevel; |
@@ -259,6 +275,10 @@ start_inferior (char **argv) |
signal (SIGTTIN, SIG_DFL); |
#endif |
+ /* Clear this so the backend doesn't get confused, thinking |
+ CONT_THREAD died, and it needs to resume all threads. */ |
+ cont_thread = null_ptid; |
+ |
signal_pid = create_inferior (new_argv[0], new_argv); |
/* FIXME: we don't actually know at this point that the create |
@@ -284,7 +304,7 @@ start_inferior (char **argv) |
resume_info.kind = resume_continue; |
resume_info.sig = 0; |
- mywait (pid_to_ptid (signal_pid), &last_status, 0, 0); |
+ last_ptid = mywait (pid_to_ptid (signal_pid), &last_status, 0, 0); |
if (last_status.kind != TARGET_WAITKIND_STOPPED) |
return signal_pid; |
@@ -293,17 +313,15 @@ start_inferior (char **argv) |
{ |
(*the_target->resume) (&resume_info, 1); |
- mywait (pid_to_ptid (signal_pid), &last_status, 0, 0); |
+ last_ptid = mywait (pid_to_ptid (signal_pid), &last_status, 0, 0); |
if (last_status.kind != TARGET_WAITKIND_STOPPED) |
return signal_pid; |
current_inferior->last_resume_kind = resume_stop; |
current_inferior->last_status = last_status; |
} |
- while (last_status.value.sig != TARGET_SIGNAL_TRAP); |
+ while (last_status.value.sig != GDB_SIGNAL_TRAP); |
- current_inferior->last_resume_kind = resume_stop; |
- current_inferior->last_status = last_status; |
return signal_pid; |
} |
@@ -338,6 +356,10 @@ attach_inferior (int pid) |
whichever we were told to attach to. */ |
signal_pid = pid; |
+ /* Clear this so the backend doesn't get confused, thinking |
+ CONT_THREAD died, and it needs to resume all threads. */ |
+ cont_thread = null_ptid; |
+ |
if (!non_stop) |
{ |
last_ptid = mywait (pid_to_ptid (pid), &last_status, 0, 0); |
@@ -346,8 +368,8 @@ attach_inferior (int pid) |
process using the "attach" command, but this is different; it's |
just using "target remote". Pretend it's just starting up. */ |
if (last_status.kind == TARGET_WAITKIND_STOPPED |
- && last_status.value.sig == TARGET_SIGNAL_STOP) |
- last_status.value.sig = TARGET_SIGNAL_TRAP; |
+ && last_status.value.sig == GDB_SIGNAL_STOP) |
+ last_status.value.sig = GDB_SIGNAL_TRAP; |
current_inferior->last_resume_kind = resume_stop; |
current_inferior->last_status = last_status; |
@@ -427,7 +449,7 @@ handle_general_set (char *own_buf) |
{ |
if (strncmp ("QPassSignals:", own_buf, strlen ("QPassSignals:")) == 0) |
{ |
- int numsigs = (int) TARGET_SIGNAL_LAST, i; |
+ int numsigs = (int) GDB_SIGNAL_LAST, i; |
const char *p = own_buf + strlen ("QPassSignals:"); |
CORE_ADDR cursig; |
@@ -450,6 +472,33 @@ handle_general_set (char *own_buf) |
return; |
} |
+ if (strncmp ("QProgramSignals:", own_buf, strlen ("QProgramSignals:")) == 0) |
+ { |
+ int numsigs = (int) GDB_SIGNAL_LAST, i; |
+ const char *p = own_buf + strlen ("QProgramSignals:"); |
+ CORE_ADDR cursig; |
+ |
+ program_signals_p = 1; |
+ |
+ p = decode_address_to_semicolon (&cursig, p); |
+ for (i = 0; i < numsigs; i++) |
+ { |
+ if (i == cursig) |
+ { |
+ program_signals[i] = 1; |
+ if (*p == '\0') |
+ /* Keep looping, to clear the remaining signals. */ |
+ cursig = -1; |
+ else |
+ p = decode_address_to_semicolon (&cursig, p); |
+ } |
+ else |
+ program_signals[i] = 0; |
+ } |
+ strcpy (own_buf, "OK"); |
+ return; |
+ } |
+ |
if (strcmp (own_buf, "QStartNoAckMode") == 0) |
{ |
if (remote_debug) |
@@ -525,6 +574,30 @@ handle_general_set (char *own_buf) |
&& handle_tracepoint_general_set (own_buf)) |
return; |
+ if (strncmp ("QAgent:", own_buf, strlen ("QAgent:")) == 0) |
+ { |
+ char *mode = own_buf + strlen ("QAgent:"); |
+ int req = 0; |
+ |
+ if (strcmp (mode, "0") == 0) |
+ req = 0; |
+ else if (strcmp (mode, "1") == 0) |
+ req = 1; |
+ else |
+ { |
+ /* We don't know what this value is, so complain to GDB. */ |
+ sprintf (own_buf, "E.Unknown QAgent value"); |
+ return; |
+ } |
+ |
+ /* Update the flag. */ |
+ use_agent = req; |
+ if (remote_debug) |
+ fprintf (stderr, "[%s agent]\n", req ? "Enable" : "Disable"); |
+ write_ok (own_buf); |
+ return; |
+ } |
+ |
/* Otherwise we didn't know what packet it was. Say we didn't |
understand it. */ |
own_buf[0] = 0; |
@@ -807,7 +880,7 @@ handle_search_memory (char *own_buf, int packet_len) |
/* Handle monitor commands not handled by target-specific handlers. */ |
static void |
-handle_monitor_command (char *mon) |
+handle_monitor_command (char *mon, char *own_buf) |
{ |
if (strcmp (mon, "set debug 1") == 0) |
{ |
@@ -941,10 +1014,6 @@ handle_qxfer_libraries (const char *annex, |
if (annex[0] != '\0' || !target_running ()) |
return -1; |
- /* Do not confuse this packet with qXfer:libraries-svr4:read. */ |
- if (the_target->qxfer_libraries_svr4 != NULL) |
- return 0; |
- |
/* Over-estimate the necessary memory. Assume that every character |
in the library name must be escaped. */ |
total_len = 64; |
@@ -1091,14 +1160,11 @@ handle_qxfer_threads_proper (struct buffer *buffer) |
{ |
ptid_t ptid = thread_to_gdb_id ((struct thread_info *)thread); |
char ptid_s[100]; |
- int core = -1; |
+ int core = target_core_of_thread (ptid); |
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); |
@@ -1555,7 +1621,9 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) |
free (qsupported); |
} |
- sprintf (own_buf, "PacketSize=%x;QPassSignals+", PBUFSIZ - 1); |
+ sprintf (own_buf, |
+ "PacketSize=%x;QPassSignals+;QProgramSignals+", |
+ PBUFSIZ - 1); |
if (the_target->qxfer_libraries_svr4 != NULL) |
strcat (own_buf, ";qXfer:libraries-svr4:read+"); |
@@ -1617,6 +1685,13 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) |
strcat (own_buf, ";tracenz+"); |
} |
+ /* Support target-side breakpoint conditions and commands. */ |
+ strcat (own_buf, ";ConditionalBreakpoints+"); |
+ strcat (own_buf, ";BreakpointCommands+"); |
+ |
+ if (target_supports_agent ()) |
+ strcat (own_buf, ";QAgent+"); |
+ |
return; |
} |
@@ -1733,7 +1808,7 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) |
if (the_target->handle_monitor_command == NULL |
|| (*the_target->handle_monitor_command) (mon) == 0) |
/* Default processing. */ |
- handle_monitor_command (mon); |
+ handle_monitor_command (mon, own_buf); |
free (mon); |
return; |
@@ -1859,9 +1934,9 @@ handle_v_cont (char *own_buf) |
goto err; |
p = q; |
- if (!target_signal_to_host_p (sig)) |
+ if (!gdb_signal_to_host_p (sig)) |
goto err; |
- resume_info[i].sig = target_signal_to_host (sig); |
+ resume_info[i].sig = gdb_signal_to_host (sig); |
} |
else |
{ |
@@ -1896,9 +1971,13 @@ handle_v_cont (char *own_buf) |
if (i < n) |
resume_info[i] = default_action; |
- /* Still used in occasional places in the backend. */ |
+ /* `cont_thread' is still used in occasional places in the backend, |
+ to implement single-thread scheduler-locking. Doesn't make sense |
+ to set it if we see a stop request, or a wildcard action (one |
+ with '-1' (all threads), or 'pPID.-1' (all threads of PID)). */ |
if (n == 1 |
- && !ptid_equal (resume_info[0].thread, minus_one_ptid) |
+ && !(ptid_equal (resume_info[0].thread, minus_one_ptid) |
+ || ptid_get_lwp (resume_info[0].thread) == -1) |
&& resume_info[0].kind != resume_stop) |
cont_thread = resume_info[0].thread; |
else |
@@ -2078,7 +2157,7 @@ handle_v_kill (char *own_buf) |
if (pid != 0 && kill_inferior (pid) == 0) |
{ |
last_status.kind = TARGET_WAITKIND_SIGNALLED; |
- last_status.value.sig = TARGET_SIGNAL_KILL; |
+ last_status.value.sig = GDB_SIGNAL_KILL; |
last_ptid = pid_to_ptid (pid); |
discard_queued_stop_replies (pid); |
write_ok (own_buf); |
@@ -2140,7 +2219,7 @@ handle_v_requests (char *own_buf, int packet_len, int *new_packet_len) |
if (strncmp (own_buf, "vAttach;", 8) == 0) |
{ |
- if (!multi_process && target_running ()) |
+ if ((!extended_protocol || !multi_process) && target_running ()) |
{ |
fprintf (stderr, "Already debugging a process\n"); |
write_enn (own_buf); |
@@ -2152,7 +2231,7 @@ handle_v_requests (char *own_buf, int packet_len, int *new_packet_len) |
if (strncmp (own_buf, "vRun;", 5) == 0) |
{ |
- if (!multi_process && target_running ()) |
+ if ((!extended_protocol || !multi_process) && target_running ()) |
{ |
fprintf (stderr, "Already debugging a process\n"); |
write_enn (own_buf); |
@@ -2301,7 +2380,7 @@ gdb_wants_thread_stopped (struct inferior_list_entry *entry) |
/* Most threads are stopped implicitly (all-stop); tag that with |
signal 0. */ |
thread->last_status.kind = TARGET_WAITKIND_STOPPED; |
- thread->last_status.value.sig = TARGET_SIGNAL_0; |
+ thread->last_status.value.sig = GDB_SIGNAL_0; |
} |
} |
@@ -2356,7 +2435,7 @@ handle_status (char *own_buf) |
struct target_waitstatus status; |
status.kind = TARGET_WAITKIND_STOPPED; |
- status.value.sig = TARGET_SIGNAL_TRAP; |
+ status.value.sig = GDB_SIGNAL_TRAP; |
prepare_resume_reply (own_buf, |
all_threads.head->id, &status); |
} |
@@ -2524,8 +2603,8 @@ main (int argc, char *argv[]) |
int pid; |
char *arg_end, *port; |
char **next_arg = &argv[1]; |
- int multi_mode = 0; |
- int attach = 0; |
+ volatile int multi_mode = 0; |
+ volatile int attach = 0; |
int was_running; |
while (*next_arg != NULL && **next_arg == '-') |
@@ -2605,6 +2684,13 @@ main (int argc, char *argv[]) |
} |
} |
} |
+ else if (strcmp (*next_arg, "-") == 0) |
+ { |
+ /* "-" specifies a stdio connection and is a form of port |
+ specification. */ |
+ *next_arg = STDIO_CONNECTION_NAME; |
+ break; |
+ } |
else if (strcmp (*next_arg, "--disable-randomization") == 0) |
disable_randomization = 1; |
else if (strcmp (*next_arg, "--no-disable-randomization") == 0) |
@@ -2635,6 +2721,12 @@ main (int argc, char *argv[]) |
exit (1); |
} |
+ /* 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 |
+ start_inferior. */ |
+ remote_prepare (port); |
+ |
bad_attach = 0; |
pid = 0; |
@@ -2706,7 +2798,14 @@ main (int argc, char *argv[]) |
if (setjmp (toplevel)) |
{ |
- detach_or_kill_for_exit (); |
+ /* If something fails and longjmps while detaching or killing |
+ inferiors, we'd end up here again, stuck in an infinite loop |
+ trap. Be sure that if that happens, we exit immediately |
+ instead. */ |
+ if (setjmp (toplevel) == 0) |
+ detach_or_kill_for_exit (); |
+ else |
+ fprintf (stderr, "Detach or kill failed. Exiting\n"); |
exit (1); |
} |
@@ -2722,8 +2821,6 @@ main (int argc, char *argv[]) |
exit (1); |
} |
- remote_prepare (port); |
- |
while (1) |
{ |
noack_mode = 0; |
@@ -2754,8 +2851,20 @@ main (int argc, char *argv[]) |
if (exit_requested || run_once) |
{ |
- detach_or_kill_for_exit (); |
- exit (0); |
+ /* If something fails and longjmps while detaching or |
+ killing inferiors, we'd end up here again, stuck in an |
+ infinite loop trap. Be sure that if that happens, we |
+ exit immediately instead. */ |
+ if (setjmp (toplevel) == 0) |
+ { |
+ detach_or_kill_for_exit (); |
+ exit (0); |
+ } |
+ else |
+ { |
+ fprintf (stderr, "Detach or kill failed. Exiting\n"); |
+ exit (1); |
+ } |
} |
fprintf (stderr, |
@@ -2791,6 +2900,56 @@ main (int argc, char *argv[]) |
} |
} |
+/* 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. */ |
+ |
+static void |
+process_point_options (CORE_ADDR point_addr, char **packet) |
+{ |
+ char *dataptr = *packet; |
+ int persist; |
+ |
+ /* Check if data has the correct format. */ |
+ if (*dataptr != ';') |
+ return; |
+ |
+ dataptr++; |
+ |
+ while (*dataptr) |
+ { |
+ if (*dataptr == ';') |
+ ++dataptr; |
+ |
+ if (*dataptr == 'X') |
+ { |
+ /* Conditional expression. */ |
+ fprintf (stderr, "Found breakpoint condition.\n"); |
+ add_breakpoint_condition (point_addr, &dataptr); |
+ } |
+ else if (strncmp (dataptr, "cmds:", strlen ("cmds:")) == 0) |
+ { |
+ dataptr += strlen ("cmds:"); |
+ if (debug_threads) |
+ fprintf (stderr, "Found breakpoint commands %s.\n", dataptr); |
+ persist = (*dataptr == '1'); |
+ dataptr += 2; |
+ add_breakpoint_commands (point_addr, &dataptr, persist); |
+ } |
+ else |
+ { |
+ /* Unrecognized token, just skip it. */ |
+ fprintf (stderr, "Unknown token %c, ignoring.\n", |
+ *dataptr); |
+ } |
+ |
+ /* Skip tokens until we find one that we recognize. */ |
+ while (*dataptr && *dataptr != 'X' && *dataptr != ';') |
+ dataptr++; |
+ } |
+ *packet = dataptr; |
+} |
+ |
/* Event loop callback that handles a serial event. The first byte in |
the serial buffer gets us here. We expect characters to arrive at |
a brisk pace, so we read the rest of the packet with a blocking |
@@ -2851,7 +3010,7 @@ process_serial_event (void) |
pid = |
ptid_get_pid (((struct inferior_list_entry *) current_inferior)->id); |
- if (tracing && disconnected_tracing) |
+ if ((tracing && disconnected_tracing) || any_persistent_commands ()) |
{ |
struct thread_resume resume_info; |
struct process_info *process = find_process_pid (pid); |
@@ -2862,9 +3021,15 @@ process_serial_event (void) |
break; |
} |
- fprintf (stderr, |
- "Disconnected tracing in effect, " |
- "leaving gdbserver attached to the process\n"); |
+ if (tracing && disconnected_tracing) |
+ fprintf (stderr, |
+ "Disconnected tracing in effect, " |
+ "leaving gdbserver attached to the process\n"); |
+ |
+ if (any_persistent_commands ()) |
+ fprintf (stderr, |
+ "Persistent commands are present, " |
+ "leaving gdbserver attached to the process\n"); |
/* Make sure we're in non-stop/async mode, so we we can both |
wait for an async socket accept, and handle async target |
@@ -3065,8 +3230,8 @@ process_serial_event (void) |
case 'C': |
require_running (own_buf); |
convert_ascii_to_int (own_buf + 1, &sig, 1); |
- if (target_signal_to_host_p (sig)) |
- signal = target_signal_to_host (sig); |
+ if (gdb_signal_to_host_p (sig)) |
+ signal = gdb_signal_to_host (sig); |
else |
signal = 0; |
myresume (own_buf, 0, signal); |
@@ -3074,8 +3239,8 @@ process_serial_event (void) |
case 'S': |
require_running (own_buf); |
convert_ascii_to_int (own_buf + 1, &sig, 1); |
- if (target_signal_to_host_p (sig)) |
- signal = target_signal_to_host (sig); |
+ if (gdb_signal_to_host_p (sig)) |
+ signal = gdb_signal_to_host (sig); |
else |
signal = 0; |
myresume (own_buf, 1, signal); |
@@ -3113,7 +3278,22 @@ process_serial_event (void) |
case '4': /* access watchpoint */ |
require_running (own_buf); |
if (insert && the_target->insert_point != NULL) |
- res = (*the_target->insert_point) (type, addr, len); |
+ { |
+ /* Insert the breakpoint. If it is already inserted, nothing |
+ will take place. */ |
+ res = (*the_target->insert_point) (type, addr, len); |
+ |
+ /* GDB may have sent us a list of *point parameters to be |
+ evaluated on the target's side. Read such list here. If we |
+ already have a list of parameters, GDB is telling us to drop |
+ that list and use this one instead. */ |
+ if (!res && (type == '0' || type == '1')) |
+ { |
+ /* Remove previous conditions. */ |
+ clear_gdb_breakpoint_conditions (addr); |
+ process_point_options (addr, &dataptr); |
+ } |
+ } |
else if (!insert && the_target->remove_point != NULL) |
res = (*the_target->remove_point) (type, addr, len); |
break; |
@@ -3145,7 +3325,7 @@ process_serial_event (void) |
if (extended_protocol) |
{ |
last_status.kind = TARGET_WAITKIND_EXITED; |
- last_status.value.sig = TARGET_SIGNAL_KILL; |
+ last_status.value.sig = GDB_SIGNAL_KILL; |
return 0; |
} |
else |
@@ -3189,7 +3369,7 @@ process_serial_event (void) |
else |
{ |
last_status.kind = TARGET_WAITKIND_EXITED; |
- last_status.value.sig = TARGET_SIGNAL_KILL; |
+ last_status.value.sig = GDB_SIGNAL_KILL; |
} |
return 0; |
} |
@@ -3313,7 +3493,7 @@ handle_target_event (int err, gdb_client_data client_data) |
resume_info.thread = last_ptid; |
resume_info.kind = resume_continue; |
- resume_info.sig = target_signal_to_host (last_status.value.sig); |
+ resume_info.sig = gdb_signal_to_host (last_status.value.sig); |
(*the_target->resume) (&resume_info, 1); |
} |
else if (debug_threads) |