X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=gdb%2Fgdbserver%2Fserver.c;h=22f25c54145135b19857c04d37bb89e3195807d6;hb=30d5032895e63800c5088101fb662b6b293e224a;hp=566e47dc2223ec5d33f66b5a0f5fc437a4e055e2;hpb=c631402292d43f01daa982c42c82f96e39659d99;p=binutils-gdb.git diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c index 566e47dc222..22f25c54145 100644 --- a/gdb/gdbserver/server.c +++ b/gdb/gdbserver/server.c @@ -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, "\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, "\n", + ptid_s, core_s); + } + else + { + buffer_xml_printf (buffer, "\n", + ptid_s); + } + } + + buffer_grow_str0 (buffer, "\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,28 +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, ";")) - { - if (strcmp (p, "multiprocess+") == 0) - { - /* GDB supports and wants multi-process support if - possible. */ - if (target_supports_multi_process ()) - 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); @@ -1116,6 +1469,20 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) 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; } @@ -1172,7 +1539,7 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) if (err == 0) { - sprintf (own_buf, "%llx", address); + strcpy (own_buf, paddress(address)); return; } else if (err > 0) @@ -1184,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) + { + strcpy (own_buf, paddress(tlb)); + return; + } + else if (n == 0) + { + write_enn (own_buf); + return; + } + return; + } + /* Handle "monitor" commands. */ if (strncmp ("qRcmd,", own_buf, 6) == 0) { @@ -1206,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; @@ -1275,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; @@ -1385,6 +1779,10 @@ handle_v_cont (char *own_buf) last_ptid = mywait (minus_one_ptid, &last_status, 0, 1); 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))); } return; @@ -1523,8 +1921,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; @@ -1639,7 +2039,7 @@ handle_v_requests (char *own_buf, int packet_len, int *new_packet_len) /* Resume inferior and wait for another event. In non-stop mode, don't really wait here, but return immediatelly to the event loop. */ -void +static void myresume (char *own_buf, int step, int sig) { struct thread_resume resume_info[2]; @@ -1683,6 +2083,10 @@ myresume (char *own_buf, int step, int sig) last_ptid = mywait (minus_one_ptid, &last_status, 0, 1); 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))); } } @@ -1692,31 +2096,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 @@ -1724,9 +2178,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 @@ -1735,9 +2188,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"); } @@ -1747,7 +2210,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); @@ -2042,6 +2505,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); @@ -2104,7 +2569,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); @@ -2119,7 +2585,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), @@ -2132,9 +2598,37 @@ main (int argc, char *argv[]) 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 (); + } + } } } @@ -2143,7 +2637,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; @@ -2169,9 +2663,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; @@ -2197,7 +2691,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 @@ -2309,27 +2845,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); @@ -2337,8 +2898,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); @@ -2381,38 +2942,26 @@ process_serial_event (void) int len = strtol (lenptr + 1, &dataptr, 16); char type = own_buf[1]; int res; - const int insert_ = ch == 'Z'; - - /* Type: '0' - software-breakpoint - '1' - hardware-breakpoint - '2' - write watchpoint - '3' - read watchpoint - '4' - access watchpoint */ + const int insert = ch == 'Z'; - if (the_target->insert_watchpoint == NULL - || the_target->remove_watchpoint == NULL) - res = 1; /* Not supported. */ - else - switch (type) - { - case '2': - /* Fallthrough. */ - case '3': - /* Fallthrough. */ - case '4': - require_running (own_buf); - /* Fallthrough. */ - case '0': - /* Fallthrough. */ - case '1': - res = insert_ ? (*the_target->insert_watchpoint) (type, addr, - len) - : (*the_target->remove_watchpoint) (type, addr, - len); - break; - default: - res = -1; /* Unrecognized type. */ - } + /* Default to unrecognized/unsupported. */ + res = 1; + switch (type) + { + case '0': /* software-breakpoint */ + case '1': /* hardware-breakpoint */ + case '2': /* write watchpoint */ + case '3': /* read watchpoint */ + case '4': /* access watchpoint */ + require_running (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; + } if (res == 0) write_ok (own_buf); @@ -2428,7 +2977,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); @@ -2439,13 +2988,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; @@ -2486,7 +3033,7 @@ process_serial_event (void) last_status.kind = TARGET_WAITKIND_EXITED; last_status.value.sig = TARGET_SIGNAL_KILL; } - return; + return 0; } else { @@ -2527,27 +3074,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) @@ -2558,11 +3113,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 = target_signal_to_host (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; }