summaryrefslogtreecommitdiff
path: root/gdb/gdbserver/server.c
diff options
context:
space:
mode:
authorPedro Alves <pedro@codesourcery.com>2008-11-25 20:47:19 +0000
committerPedro Alves <pedro@codesourcery.com>2008-11-25 20:47:19 +0000
commit95035af3c98af4ea5f905ef1ce576454862699e7 (patch)
tree52a08bb74efe878070f5416f95761204280e1608 /gdb/gdbserver/server.c
parent8b11acc6150c279279bc286f4298e79586f171e6 (diff)
downloadgdb-cvs/multiprocess-20081120-branch.tar.gz
2008-11-25 Pedro Alves <pedro@codesourcery.com>cvs/multiprocess-20081120-branch
2008-10-13 Pedro Alves <pedro@codesourcery.com> * server.c (discard_pending_stop_replies): Initialize prev. 2008-10-12 Pedro Alves <pedro@codesourcery.com> * linux-low.c (linux_wait_1): Cancel breakpoint hits in threads we're not reporting. (cancel_breakpoints_callback): New. 2008-10-12 Pedro Alves <pedro@codesourcery.com> * server.c (queue_stop_reply_callback, handle_status): Pass TARGET_SIGNAL_TRAP, not TARGET_SIGNAL_0. * linux-low.c (ptid_is_pid): Delete. (linux_wait_for_event): In non-stop, don't set the current inferior arbitrarily. (linux_wait): Flush the event pipe before polling for an event. (wait_for_sigstop): In non-stop, don't set the current inferior arbitrarily. (linux_resume_one_lwp): Small cleanup. (cancel_breakpoint): New. (linux_resume_one_thread): Use it. Still report SIGTRAPs. (regsets_store_inferior_registers): Plug leak. (sigchld_handler): Don't use fprintf here. * mem-break.c (breakpoint_at): New. * inferiors.c (ptid_is_pid): New. * mem-break.h (breakpoint_at): Declare. * server.c (discard_queued_stop_replies): Add `pid' argument. Handle it. (send_next_stop_reply): Cleanup. (attach_inferior): Don't wait here in non-stop mode. (handle_v_attach): Don't queue stop replies here. (handle_v_kill): Discard queued stop replies of the inferior we just killed. (queue_stop_reply_callback): Add `arg' argument. Handle it. (handle_status): Adjust. (kill_inferior_callback): Discard queued stop replies of the inferior we just killed. (detach_or_kill_inferior_callback): Discard queued stop replies of the inferior we just killed or detached from. (process_serial_event): Cleanup. Discard queued stop replies of the inferior we just detached. Don't write 0 bytes to the inferior. (handle_serial_event): Debug output. * server.h (ptid_is_pid): Declare. * remote-utils.c (prepare_resume_reply): Avoid reading registers and memory from a thread that is gone. 2008-09-26 Pedro Alves <pedro@codesourcery.com> * server.h (struct sym_cache, struct process_info_private): Forward declare. (struct process_info): Add symbol_cache, all_symbols_looked_up and private fields. (current_process): Declare. * remote-utils.c (struct sym_cache) <name>: Remove constness. (symbol_cache): Delete. (free_sym_cache, clear_symbol_cache): New. (look_up_one_symbol): Adjust, to per-process symbol cache. * inferiors.c (current_process): New. * linux-low.h: Include "gdb_proc_service.h". (struct process_info_private): Define. * linux-low.c (thread_db_active): Delete. (linux_add_process): New. (handle_extended_wait, linux_create_inferior, linux_attach): Use it. (linux_wait_for_event, linux_look_up_symbols): Adjust. (initialize_low): Don't clear the global thread_db_active. * thread-db.c (proc_handle, thread_agent): Delete. (fixup_proc_handle): New. (thread_db_err_str, thread_db_enable_reporting): Use it. Adjust. (thread_db_find_new_threads): Look for the current inferior thread, not the first thread in the list. Use fixup_proc_handle. Adjust. (thread_db_get_tls_address): Use fixup_proc_handle. (thread_db_init): Likewise. Adjust. 2008-09-26 Pedro Alves <pedro@codesourcery.com> * mem-break.c (get_breakpoint_list): Add `create' argument. Only create the list if create is set. (remove_breakpoint_list): New. (set_breakpoint_at, find_breakpoint_at, check_mem_read) (check_mem_write, delete_all_breakpoints): Adjust. 2008-09-26 Pedro Alves <pedro@codesourcery.com> * server.c (gdbserver_usage): Describe --remote-debug option. (main): Handle --remote-debug switch. 2008-09-19 Pedro Alves <pedro@codesourcery.com> Non-stop mode support. * linux-low.c (linux_event_pipe): New int array for pipe. (target_is_async_p): New. (handle_extended_wait): Use my_waitpid. (linux_kill_one_lwp): Stop the LWP if it is not stopped already. Use my_waitpid, not linux_wait_for_event. Remove the lwp and the corresponding thread from the lists. (linux_kill_one_process, linux_kill): Delete. (linux_kill_1): Rename back to ... (linux_kill) ... this. Stop the LWP if it is not stopped already. Use my_waitpid, not linux_wait_for_event. Remove the lwp and the corresponding thread from the lists. (linux_detach_one_lwp): Make sure the LWP is stopped. Adjust to new linux_wait_for_event interface. (linux_detach_one_process, linux_detach): Delete. (linux_detach_1): Rename back to ... (linux_detach): This. (linux_join): Add PID argument. Use my_waitpid instead of waitpid. (status_pending_p): Ignore suspended threads. (my_waitpid): Emulate __WALL. (linux_wait_for_lwp): Add 'options' argument. Handle it. Use my_wait. If requesting an event from a specific process, leave events in other processes pending. (resume_stopped_lwps): New. (linux_wait_for_event): Delete. (linux_wait_for_event_1): Rename back to ... (linux_wait_for_event): ... this. Change interface: add wstat and options arguments, return -1 on error, 0 otherwise. Adjust. In all-stop, resume stopped lwps if there was no pending status. Don't return immediatelly if a pending status was found --- continue handling it instead. Don't loop, only handle one event. (linux_wait): Rename to ... (linux_wait_1): ... this. Add target_options argument. Handle it. Don't loop --- only handle one event. Ignore the continue thread in non_stop mode. Adjust to new resume_kind interface. Only stop all threads in all-stop mode. (async_file_flush, async_file_mark): New. (linux_wait): New. (wait_for_sigstop): Set stopping_threads here. Use linux_wait_for_lwp instead of linux_wait_for_event. Adjust. (stop_all_lwps): Don't set stopping_threads here. (resume_ptr): Delete. (struct resume_info): New. (linux_set_resume_request): Add arg argument. Adjust to take a struct remove_info instead of the global resume_ptr. Accept pid,-1 to apply to all threads. (linux_continue_one_thread, linux_queue_one_thread): Merge both and create ... (linux_resume_one_thread): ... this. New. Handle rk_stop. (resume_status_pending_p): Assume no resume info to mean do nothing. Ignore suspended LWPs. (linux_resume): Add n argument. Adjust. In non-stop mode, don't look for a pending status over all threads. (linux_read_offsets): Minor cleanup. (sigchld_handler, linux_async, linux_start_non_stop): New. (linux_target_ops): Register linux_async and linux_start_non_stop. (initialize_low): Register sigchld_handler as SIGCHLD handler. * utils.c (internal_verror, internal_error_file_line): New. * Makefile.in (SFILES): Add event-loop.c. (OBS): Add event-loop.o. (event-loop.o): New rule. * linux-low.h (struct lwp_info) <suspended>: New flag. * thread-db.c (thread_db_create_event): Make sure thread_db reads from the current inferior. (thread_db_get_tls_address): Comment. * server.c (thread_from_wait, old_thread_from_wait, attached): Delete. (non_stop): New global. (own_buf, mem_buf): New globals. (struct vstop_notif): New. (notif_queue): New. (queue_stop_reply, push_event, discard_queued_stop_replies) (send_next_stop_reply): New. (start_inferior): Adjust to new thread_resume interface. Adjust to new mywait interface. (attach_inferior): Adjust. (handle_general_set): Handle QNonStop. (handle_query): Pass 'QNonStop+'. (handle_v_cont): Handle vCont;t. Don't enable/disable async io in non-stop mode. In non-stop return OK, and don't wait for the target. (handle_v_attach): In non-stop, return OK, and queue events for all threads. (handle_v_run): In non-stop, set the general thread here. (handle_v_stopped): New. (handle_v_requests): Report support for 't'. Handle 'vStopped'. (proceed): Add comment. Adjust. In non-stop, don't enable/disable async io; write 'OK', and don't wait for the target. (queue_stop_reply_callback, handle_status): New. (kill_inferior_callback, detach_or_kill_inferior_callback) (join_inferiors_callback): New. (main): In --debug mode, also enable remote debug. Don't pass -1 to kill_inferior or detach_inferior; instead, iterate over all processes killing or detaching them. Adjust to use the even-loop. (process_serial_event): New, factored out of main. If the connection closed, remove all sources from the event loop. Iterate over all inferiors joining them. Use handle_status. Don't print inferior exit notices here. In non-stop, defer exiting until GDB read all queued events. (handle_serial_event, handle_target_event): New. * server.h (FOR_EACH_INFERIOR): New. (thread_from_wait, old_thread_from_wait): Remove. (non_stop): Declare. (gdb_client_data, handler_func): New typedefs. (delete_file_handler, add_file_handler, start_event_loop) (handle_serial_event, handle_target_event, push_event) (putpkt_notif, internal_error_file_line): Declare. (internal_error): Define. * target.c (mywait): Add `options' argument. Print inferior exit notices here. (start_non_stop): New. * event-loop.c: New. * remote-utils.c (remote_open): Register remote_desc in the event loop, with handle_serial_event as callback. (remote_close): Remove remote_desc from the event loop. (hex_or_minus_one): New. (read_ptid, read_ptid): Use it. (putpkt_binary): Rename to ... (putpkt_binary_1): ... this. Add `notif' argument. Handle pushing a remote protocol notification. (putpkt_binary): New as wrapper around putpkt_binary_1. (putpkt_notif): New. (getpkt): Debug output. (prepare_resume_reply): Remove dead code. In non-stop, don't set the general thread here. * target.h (enum resume_kind): New. (struct thread_resume) <leave_stopped, step>: Delete. (struct thread_resume) <kind>: New field. (TARGET_WNOHANG): Define. (struct target_ops) <kill, detach>: Adjust comments. (struct target_ops) <join>: Add `pid' argument. (struct target_ops) <resume>: Add `n' argument. (struct target_ops) <wait>: Add `options' argument. (struct target_ops) <async, start_non_stop>: New fields. (join_inferior): Add `pid' argument. (target_async): New. (start_non_stop): Declare. (mywait): Add options argument. 2008-09-15 Pedro Alves <pedro@codesourcery.com> * linux-low.c (new_inferior): Mention in the comment that all inferiors should have the same architecture for now. (linux_create_inferior, linux_attach): Only set new_inferior if this is the first process. 2008-08-29 Pedro Alves <pedro@codesourcery.com> * linux-low.c (ptid_is_pid): Move higher. (linux_wait_for_lwp): Remove dead code. (linux_wait_for_event): Rename to ... (linux_wait_for_event_1): ... this. (linux_wait_for_event): New. (ptid_same_pid): Delete. (linux_set_resume_request): Clearify. 2008-08-28 Pedro Alves <pedro@codesourcery.com> * linux-low.c (handle_extended_wait, linux_attach_lwp) (linux_attach): Minor cleanups. 2008-08-28 Pedro Alves <pedro@codesourcery.com> * linux-low.c (linux_thread_alive): Return false for a listed thread known to be dead. (linux_wait_for_event): Don't set the dead flag here. (wait_for_sigstop): Store ptid before waiting for the event. Minor cleanup. 2008-08-28 Pedro Alves <pedro@codesourcery.com> * inferiors.c (find_inferior): Allow deleting the current iterated inferior. 2008-08-28 Pedro Alves <pedro@codesourcery.com> * linux-low.c (linux_pid_to_exec_file): Move higher. (linux_enable_event_reporting): Enable PTRACE_O_TRACEEXEC. (handle_extended_wait): Handle PTRACE_EVENT_EXEC. * remote-utils.c (prepare_resume_reply): Set the general thread to the last thread that had an event in TARGET_WAITKIND_FORKED and TARGET_WAITKIND_VFORKED. Handle TARGET_WAITKIND_EXECD. 2008-08-28 Pedro Alves <pedro@codesourcery.com> * server.c (handle_query): Pass "QExecFile:PID;" back in the reply. 2008-08-28 Pedro Alves <pedro@codesourcery.com> * linux-low.c (linux_supports_tracefork_flag): Move higher. (linux_enable_event_reporting): New. (handle_extended_wait): Change return type to int. Handle PTRACE_EVENT_FORK and PTRACE_EVENT_VFORK. (add_lwp): Set waitstatus.kind to TARGET_WAITKIND_IGNORE. (linux_attach_lwp): Use linux_enable_event_reporting. (linux_wait_for_event): Don't keep waiting if the extended wait status should be reported to gdb. (linux_wait): Use linux_enable_event_reporting. If waitstatus holds a processed event, return it instead. * remote-utils.c (prepare_resume_reply): Handle TARGET_WAITKIND_FORKED and TARGET_WAITKIND_VFORKED. * linux-low.h (struct lwp_info) <waitstatus>: New member. 2008-08-28 Pedro Alves <pedro@codesourcery.com> * target.h (struct target_ops) <pid_to_exec_file>: New member. * server.c (handle_query): Handle qExecFile. * linux-low.c (linux_pid_to_exec_file): New. (linux_target_ops): Register it. 2008-08-28 Pedro Alves <pedro@codesourcery.com> * linux-low.c (is_lwpid): New. (linux_kill_one_lwp, linux_kill_1, linux_detach_one_lwp): Adjust. (status_pending_p): Check if we're interested in this lwp. (linux_wait_for_lwp): Change signature: return an lwp_info*, and take a ptid instead of an lwp_info**. (linux_wait_for_event): Take a ptid instead of a thread_info pointer. Adjust. (wait_for_sigstop): Adjust. If a whole process died, keep the exit status pending. (ptid_is_pid, ptid_same_pid): New. (linux_set_resume_request): Allow resuming all threads of a process. (resume_status_pending_p): Check for dead lwps. * linux-low.h (struct lwp_info) <dead>: New field. * server.c (start_inferior): Only resume and wait for events from the inferior we're creating. 2008-08-28 Pedro Alves <pedro@codesourcery.com> Decouple target code from remote protocol. * linux-low.c (linux_wait): Change prototype. Adjust. * server.c (last_status, last_ptid): New. (start_inferior): Remove "statusptr" argument. Adjust. (attach_inferior, handle_v_cont, handle_v_attach, handle_v_run) (handle_v_kill, handle_v_requests): Remove "status" and "signal" parameters. Adjust. (myresume): Rename to ... (proceed): ... this. Remove "statusp" parameter. Adjust. (main): Remove "status" local. Adjust. * target.c (mywait): Change prototype. Adjust. * target.h (enum target_waitkind): New. (struct target_waitstatus): New. (struct target_ops) <wait>: Change prototype. (mywait): Adjust. * remote-utils.c: Include "target.h". (prepare_resume_reply): Change prototype to take a ptid and a target_waitstatus. Adjust. * server.h (prepare_resume_reply): Adjust prototype. 2008-08-28 Pedro Alves <pedro@codesourcery.com> * inferiors.c (all_processes): New. (add_process): New. * linux-low.c (linux_create_inferior): Add process. (linux_attach_lwp): Add "initial" parameter, and use it instead of relying on having only one thread in the global list. (linux_attach): Add process. (struct counter): New. (check_if_last_thread_of_pid, is_last_thread_of_process): New. (linux_kill_one_lwp): Add ARGS parameter. Change return type to int. Check if we're interested in this lwp. Use is_last_thread_of_process. (linux_kill): Rename to ... (linux_kill_1): ... this. Kill lwps of the requested only. (linux_kill_one_process): New. (linux_kill): New. (linux_detach_one_lwp): Add ARGS parameter. Change return type to int. Check if we're interested in this lwp. Remove the lwp from both the lwp list and the thread list. (any_thread_of, linux_detach_1, linux_detach_one_process): New. (linux_detach): Reimplement. (linux_wait_for_event): Use is_last_thread_of_process. (linux_wait): Likewise. On process exit, don't clear all inferiors. Implement multi-process extensions. * mem-break.c (breakpoints): Delete. (struct breakpoint_list): New. (all_breakpoints): New. (get_breakpoint_list): New. (set_breakpoint_at, delete_breakpoint, find_breakpoint_at) (check_mem_read, check_mem_write, delete_all_breakpoints): Use it. * server.h (struct process_info): New. (all_processes): Declare. (add_process): Declare. * linux-low.h (linux_attach_lwp): Add "initial" parameter. * thread-db.c (maybe_attach_thread): Adjust. 2008-08-28 Pedro Alves <pedro@codesourcery.com> * linux-low.c, linux-low.h, proc-service.c, thread-db.c: Rename `struct process_info' to `struct lwp_info', and adjust throughout. 2008-08-28 Pedro Alves <pedro@codesourcery.com> Implement remote protocol multi-process extensions. * inferiors.c (null_ptid, minus_one_ptid): New. (ptid_build, pid_to_ptid, ptid_get_pid, ptid_get_lwp) (ptid_get_tid, ptid_equal): New functions. (add_thread): Drop gdb_id argument. Retype thread_id argument to ptid_t. Adjust. (thread_id_to_gdb_id): Adjust. (thread_to_gdb_id): Change return type to ptid_t. Adjust. (gdb_id_to_thread): Rename to ... (find_thread_pid): ... this. Adjust. (gdb_id_to_thread_id): Change return type to ptid_t. Adjust. (find_inferior_id): Change id argument type to ptid_t. Adjust. (loaded_dll, add_pid_to_list, pull_pid_from_list): Adjust. (initialize_inferiors): New. * remote-utils.c (hexchars): New. (ishex, unpack_varlen_hex, write_ptid, read_ptid): New. (prepare_resume_reply): Adjust. * server.c (cont_thread, general_thread, step_thread) (thread_from_wait, old_thread_from_wait): Change type to ptid_t. (multi_process): New. (start_inferior): Adjust. (handle_query): Adjust. Report multiprocess extensions support. (handle_v_cont): Adjust. (handle_v_kill): New. (handle_v_requests): Handle vKill. (myresume): Adjust. (first_thread_of): New. (main): Call initialize_inferiors. If bailing out, kill all inferiors. Handle multi-process detach. Handle multi-process H and T. * server.h (ULONGEST): New typedef. (struct ptid): New struct. (ptid_t): New typedef. (minus_one_ptid, null_ptid): New. (ptid_t ptid_build, pid_to_ptid, ptid_get_pid, ptid_get_lwp) (ptid_get_tid, ptid_equal): New. (struct inferior_list_entry) <id>: Change type to ptid_t. (add_thread, thread_id_to_gdb_id, thread_to_gdb_id) (gdb_id_to_thread_id): Adjust prototypes. (find_thread_pid): Declare. (find_inferior_id): Adjust prototype. (cont_thread, general_thread, step_thread, thread_from_wait) (old_thread_from_wait): Adjust type to ptid_t. (multi_process): Declare. (read_ptid, write_ptid): Declare. * linux-low.c (pid_of): Adjust. (lwpid_of): New. (inferior_lwpid): New. (handle_extended_wait): Adjust. (add_process): Change pid argument to a ptid. Adjust. (linux_create_inferior): Adjust. (linux_attach_lwp): Adjust. Clear new_inferior on error. If creating a new inferior, don't rely on inferior_pid, instead use the lwpid as pid. (linux_attach): Set new_inferior earlier. Adjust. (linux_kill): Change return type to int. Adjust. (linux_detach): Add pid parameter. (linux_thread_alive): Change lwpid paremeter type to ptid. Adjust. (same_lwp, find_lwp_pid): New. (linux_wait_for_process): Adjust. (linux_wait_for_process, linux_wait_for_event, send_sigstop) (wait_for_sigstop, linux_resume_one_process, (linux_resume_one_process, linux_set_resume_request) (linux_continue_one_thread, linux_queue_one_thread) (fetch_register, usr_store_inferior_registers) (regsets_fetch_inferior_registers) (regsets_store_inferior_registers, linux_read_memory) (linux_write_memory, linux_request_interrupt, linux_read_auxv): Adjust. * linux-low.h (get_process_thread): Adjust. (struct process_info) <lwpid>: Remove. (find_lwp_pid): Declare. * target.c (set_desired_inferior): Adjust. (target_pid_to_str): New. * target.h (struct thread_resume) <thread>: Change type to ptid_t. (struct target_ops) <kill>: Change return type to int, and take an int as parameter. (struct target_ops) <detach>: Take an int as parameter. (struct target_ops) <thread_alive>: Change pid argument type to ptid_t. (kill_inferior, detach_inferior): Add PID argument. * thread-db.c (thread_db_create_event): Adjust. (find_one_thread): Change argument to a ptid. Adjust. (maybe_attach_thread, thread_db_get_tls_address, thread_db_init): Adjust. * proc-service.c (ps_lgetregs): Adjust.
Diffstat (limited to 'gdb/gdbserver/server.c')
-rw-r--r--gdb/gdbserver/server.c957
1 files changed, 738 insertions, 219 deletions
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index 4adbf514ef6..4ccda469bb3 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -32,18 +32,19 @@
#include <malloc.h>
#endif
-unsigned long cont_thread;
-unsigned long general_thread;
-unsigned long step_thread;
-unsigned long thread_from_wait;
-unsigned long old_thread_from_wait;
+ptid_t cont_thread;
+ptid_t general_thread;
+ptid_t step_thread;
+
int server_waiting;
static int extended_protocol;
-static int attached;
static int response_needed;
static int exit_requested;
+int multi_process;
+int non_stop;
+
static char **program_argv, **wrapper_argv;
/* Enable miscellaneous debugging output. The name is historical - it
@@ -87,6 +88,123 @@ int disable_packet_Tthread;
int disable_packet_qC;
int disable_packet_qfThreadInfo;
+/* Last status reported to GDB. */
+static struct target_waitstatus last_status;
+static ptid_t last_ptid;
+
+static char *own_buf;
+static unsigned char *mem_buf;
+
+/* Structure holding information relative to a single stop reply. We
+ keep a queue of these (realy a singly-linked list) to push to GDB
+ in non-stop mode. */
+struct vstop_notif
+{
+ /* Pointer to next in list. */
+ struct vstop_notif *next;
+
+ /* Thread or process that got the event. */
+ ptid_t ptid;
+
+ /* Event info. */
+ struct target_waitstatus status;
+};
+
+/* The pending stop replies list head. */
+static struct vstop_notif *notif_queue = NULL;
+
+/* Put a stop reply to the stop reply queue. */
+static void
+queue_stop_reply (ptid_t ptid, struct target_waitstatus *status)
+{
+ struct vstop_notif *new_notif;
+
+ new_notif = malloc (sizeof (*new_notif));
+ new_notif->next = NULL;
+ new_notif->ptid = ptid;
+ new_notif->status = *status;
+
+ if (notif_queue)
+ {
+ struct vstop_notif *tail;
+ for (tail = notif_queue;
+ tail && tail->next;
+ tail = tail->next)
+ ;
+ tail->next = new_notif;
+ }
+ else
+ notif_queue = new_notif;
+
+ if (remote_debug)
+ {
+ int i = 0;
+ struct vstop_notif *tail;
+
+ for (tail = notif_queue; tail; tail = tail->next)
+ i++;
+
+ fprintf (stderr, "pending stop replies: %d\n", i);
+ }
+}
+
+/* Place an an event in the stop reply queue, and push a notification
+ if we aren't sending one yet. */
+void
+push_event (ptid_t ptid, struct target_waitstatus *status)
+{
+ queue_stop_reply (ptid, status);
+
+ /* If this is the first stop reply in the queue, then inform GDB
+ about it, by sending a Stop notification. */
+ if (notif_queue->next == NULL)
+ {
+ char *p = own_buf;
+ strcpy (p, "Stop:");
+ p += strlen (p);
+ prepare_resume_reply (p,
+ notif_queue->ptid, &notif_queue->status);
+ putpkt_notif (own_buf);
+ }
+}
+
+/* Get rid of the currently pending stop replies. */
+static void
+discard_queued_stop_replies (int pid)
+{
+ struct vstop_notif *prev = NULL, *reply, *next;
+
+ for (reply = notif_queue; reply; reply = next)
+ {
+ next = reply->next;
+
+ if (pid == -1
+ || ptid_get_pid (reply->ptid) == pid)
+ {
+ if (reply == notif_queue)
+ notif_queue = next;
+ else
+ prev->next = reply->next;
+
+ free (reply);
+ }
+ else
+ prev = reply;
+ }
+}
+
+/* If there are more stop replies to push, push one now. */
+static void
+send_next_stop_reply (char *own_buf)
+{
+ if (notif_queue)
+ prepare_resume_reply (own_buf,
+ notif_queue->ptid,
+ &notif_queue->status);
+ else
+ write_ok (own_buf);
+}
+
static int
target_running (void)
{
@@ -94,10 +212,9 @@ target_running (void)
}
static int
-start_inferior (char **argv, char *statusptr)
+start_inferior (char **argv)
{
char **new_argv = argv;
- attached = 0;
if (wrapper_argv != NULL)
{
@@ -141,37 +258,39 @@ start_inferior (char **argv, char *statusptr)
if (wrapper_argv != NULL)
{
struct thread_resume resume_info;
- int sig;
+ ptid_t ptid;
- resume_info.thread = -1;
- resume_info.step = 0;
+ resume_info.thread = pid_to_ptid (signal_pid);
+ resume_info.kind = rk_continue;
resume_info.sig = 0;
- resume_info.leave_stopped = 0;
- sig = mywait (statusptr, 0);
- if (*statusptr != 'T')
- return sig;
+ ptid = mywait (pid_to_ptid (signal_pid), &last_status, 0, 0);
+
+ if (last_status.kind != TARGET_WAITKIND_STOPPED)
+ return signal_pid;
do
{
- (*the_target->resume) (&resume_info);
+ (*the_target->resume) (&resume_info, 1);
- sig = mywait (statusptr, 0);
- if (*statusptr != 'T')
- return sig;
+ mywait (pid_to_ptid (signal_pid), &last_status, 0, 0);
+ if (last_status.kind != TARGET_WAITKIND_STOPPED)
+ return signal_pid;
}
- while (sig != TARGET_SIGNAL_TRAP);
+ while (last_status.value.sig != TARGET_SIGNAL_TRAP);
- return sig;
+ return signal_pid;
}
- /* Wait till we are at 1st instruction in program, return signal
- number (assuming success). */
- return mywait (statusptr, 0);
+ /* Wait till we are at 1st instruction in program, return new pid
+ (assuming success). */
+ last_ptid = mywait (pid_to_ptid (signal_pid), &last_status, 0, 0);
+
+ return signal_pid;
}
static int
-attach_inferior (int pid, char *statusptr, int *sigptr)
+attach_inferior (int pid)
{
/* myattach should return -1 if attaching is unsupported,
0 if it succeeded, and call error() otherwise. */
@@ -179,8 +298,6 @@ attach_inferior (int pid, char *statusptr, int *sigptr)
if (myattach (pid) != 0)
return -1;
- attached = 1;
-
fprintf (stderr, "Attached; pid = %d\n", pid);
fflush (stderr);
@@ -189,13 +306,17 @@ attach_inferior (int pid, char *statusptr, int *sigptr)
whichever we were told to attach to. */
signal_pid = pid;
- *sigptr = mywait (statusptr, 0);
-
- /* GDB knows to ignore the first SIGSTOP after attaching to a running
- process using the "attach" command, but this is different; it's
- just using "target remote". Pretend it's just starting up. */
- if (*statusptr == 'T' && *sigptr == TARGET_SIGNAL_STOP)
- *sigptr = TARGET_SIGNAL_TRAP;
+ if (!non_stop)
+ {
+ last_ptid = mywait (pid_to_ptid (pid), &last_status, 0, 0);
+
+ /* GDB knows to ignore the first SIGSTOP after attaching to a running
+ 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;
+ }
return 0;
}
@@ -283,6 +404,43 @@ handle_general_set (char *own_buf)
return;
}
+ if (strncmp (own_buf, "QNonStop:", 9) == 0)
+ {
+ char *mode = own_buf + 9;
+ int req = -1;
+ char *req_str;
+
+ if (strcmp (mode, "0") == 0)
+ req = 0;
+ else if (strcmp (mode, "1") == 0)
+ req = 1;
+ else
+ {
+ /* We don't know what this mode is, so complain to
+ GDB. */
+ fprintf (stderr, "Unknown non-stop mode requested: %s\n",
+ own_buf);
+ write_enn (own_buf);
+ return;
+ }
+
+ req_str = req ? "non-stop" : "all-stop";
+ if (start_non_stop (req) != 0)
+ {
+ fprintf (stderr, "Setting %s mode failed\n", req_str);
+ write_enn (own_buf);
+ return;
+ }
+
+ non_stop = req;
+
+ if (remote_debug)
+ fprintf (stderr, "[%s mode enabled]\n", req_str);
+
+ write_ok (own_buf);
+ return;
+ }
+
/* Otherwise we didn't know what packet it was. Say we didn't
understand it. */
own_buf[0] = 0;
@@ -501,10 +659,20 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
/* Reply the current thread id. */
if (strcmp ("qC", own_buf) == 0 && !disable_packet_qC)
{
+ ptid_t gdb_id;
require_running (own_buf);
- thread_ptr = all_threads.head;
- sprintf (own_buf, "QC%x",
- thread_to_gdb_id ((struct thread_info *)thread_ptr));
+
+ if (!ptid_equal (general_thread, minus_one_ptid))
+ gdb_id = general_thread;
+ else
+ {
+ thread_ptr = all_threads.head;
+ gdb_id = thread_to_gdb_id ((struct thread_info *)thread_ptr);
+ }
+
+ sprintf (own_buf, "QC");
+ own_buf += 2;
+ own_buf = write_ptid (own_buf, gdb_id);
return;
}
@@ -521,19 +689,28 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
{
if (strcmp ("qfThreadInfo", own_buf) == 0)
{
+ ptid_t gdb_id;
+
require_running (own_buf);
thread_ptr = all_threads.head;
- sprintf (own_buf, "m%x", thread_to_gdb_id ((struct thread_info *)thread_ptr));
+
+ *own_buf++ = 'm';
+ gdb_id = thread_to_gdb_id ((struct thread_info *)thread_ptr);
+ write_ptid (own_buf, gdb_id);
thread_ptr = thread_ptr->next;
return;
}
if (strcmp ("qsThreadInfo", own_buf) == 0)
{
+ ptid_t gdb_id;
+
require_running (own_buf);
if (thread_ptr != NULL)
{
- sprintf (own_buf, "m%x", thread_to_gdb_id ((struct thread_info *)thread_ptr));
+ *own_buf++ = 'm';
+ gdb_id = thread_to_gdb_id ((struct thread_info *)thread_ptr);
+ write_ptid (own_buf, gdb_id);
thread_ptr = thread_ptr->next;
return;
}
@@ -772,6 +949,21 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
if (strncmp ("qSupported", own_buf, 10) == 0
&& (own_buf[10] == ':' || own_buf[10] == '\0'))
{
+ char *p = &own_buf[10];
+
+ /* 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, ";"))
+ {
+ /* Record if GDB knows about multiprocess support. */
+ if (strcmp (p, "multiprocess+") == 0)
+ multi_process = 1;
+ }
+
sprintf (own_buf, "PacketSize=%x;QPassSignals+", PBUFSIZ - 1);
/* We do not have any hook to indicate whether the target backend
@@ -792,6 +984,11 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
if (transport_is_reliable)
strcat (own_buf, ";QStartNoAckMode+");
+
+ strcat (own_buf, ";multiprocess+");
+
+ strcat (own_buf, ";QNonStop+");
+
return;
}
@@ -800,8 +997,9 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
&& strncmp ("qGetTLSAddr:", own_buf, 12) == 0)
{
char *p = own_buf + 12;
- CORE_ADDR parts[3], address = 0;
+ CORE_ADDR parts[2], address = 0;
int i, err;
+ ptid_t ptid;
require_running (own_buf);
@@ -825,7 +1023,10 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
p2 = NULL;
}
- decode_address (&parts[i], p, len);
+ if (i == 0)
+ ptid = read_ptid (p, NULL);
+ else
+ decode_address (&parts[i - 1], p, len);
p = p2;
}
@@ -833,12 +1034,12 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
err = 1;
else
{
- struct thread_info *thread = gdb_id_to_thread (parts[0]);
+ struct thread_info *thread = find_thread_pid (ptid);
if (thread == NULL)
err = 2;
else
- err = the_target->get_tls_address (thread, parts[1], parts[2],
+ err = the_target->get_tls_address (thread, parts[0], parts[1],
&address);
}
@@ -914,6 +1115,40 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
return;
}
+ if (the_target->pid_to_exec_file != NULL
+ && strncmp ("qExecFile:", own_buf, 10) == 0)
+ {
+ char *p = own_buf + 10;
+ int pid;
+ char *path;
+ size_t len;
+
+ pid = strtol (p, NULL, 16);
+
+ path = (*the_target->pid_to_exec_file) (pid);
+
+ if (!path)
+ {
+ write_enn (own_buf);
+ return;
+ }
+
+ len = strlen (path);
+
+ if (len * 2 >= PBUFSIZ - strlen (own_buf) - 2)
+ {
+ /* File name too long for packet. */
+ write_enn (own_buf);
+ return;
+ }
+
+ own_buf[0] = 'Q';
+ p = own_buf + strlen (own_buf);
+ *p++ = ';';
+ hexify (p, path, len);
+ return;
+ }
+
/* Otherwise we didn't know what packet it was. Say we didn't
understand it. */
own_buf[0] = 0;
@@ -921,11 +1156,11 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
/* Parse vCont packets. */
void
-handle_v_cont (char *own_buf, char *status, int *signal)
+handle_v_cont (char *own_buf)
{
char *p, *q;
int n = 0, i = 0;
- struct thread_resume *resume_info, default_action;
+ struct thread_resume *resume_info;
/* Count the number of semicolons in the packet. There should be one
for every action. */
@@ -936,28 +1171,20 @@ handle_v_cont (char *own_buf, char *status, int *signal)
p++;
p = strchr (p, ';');
}
- /* Allocate room for one extra action, for the default remain-stopped
- behavior; if no default action is in the list, we'll need the extra
- slot. */
- resume_info = malloc ((n + 1) * sizeof (resume_info[0]));
- default_action.thread = -1;
- default_action.leave_stopped = 1;
- default_action.step = 0;
- default_action.sig = 0;
+ resume_info = malloc (n * sizeof (resume_info[0]));
p = &own_buf[5];
- i = 0;
while (*p)
{
p++;
- resume_info[i].leave_stopped = 0;
-
if (p[0] == 's' || p[0] == 'S')
- resume_info[i].step = 1;
+ resume_info[i].kind = rk_step;
else if (p[0] == 'c' || p[0] == 'C')
- resume_info[i].step = 0;
+ resume_info[i].kind = rk_continue;
+ else if (p[0] == 't')
+ resume_info[i].kind = rk_stop;
else
goto err;
@@ -980,17 +1207,10 @@ handle_v_cont (char *own_buf, char *status, int *signal)
}
if (p[0] == 0)
- {
- resume_info[i].thread = -1;
- default_action = resume_info[i];
-
- /* Note: we don't increment i here, we'll overwrite this entry
- the next time through. */
- }
+ resume_info[i].thread = minus_one_ptid;
else if (p[0] == ':')
{
- unsigned int gdb_id = strtoul (p + 1, &q, 16);
- unsigned long thread_id;
+ ptid_t ptid = read_ptid (p + 1, &q);
if (p == q)
goto err;
@@ -998,33 +1218,36 @@ handle_v_cont (char *own_buf, char *status, int *signal)
if (p[0] != ';' && p[0] != 0)
goto err;
- thread_id = gdb_id_to_thread_id (gdb_id);
- if (thread_id)
- resume_info[i].thread = thread_id;
- else
- goto err;
-
- i++;
+ resume_info[i].thread = ptid;
}
- }
- resume_info[i] = default_action;
+ i++;
+ }
/* Still used in occasional places in the backend. */
- if (n == 1 && resume_info[0].thread != -1)
+ if (n == 1
+ && !ptid_equal (resume_info[0].thread, minus_one_ptid)
+ && resume_info[0].kind != rk_stop)
cont_thread = resume_info[0].thread;
else
- cont_thread = -1;
+ cont_thread = minus_one_ptid;
set_desired_inferior (0);
- enable_async_io ();
- (*the_target->resume) (resume_info);
+ if (!non_stop)
+ enable_async_io ();
+
+ (*the_target->resume) (resume_info, n);
free (resume_info);
- *signal = mywait (status, 1);
- prepare_resume_reply (own_buf, *status, *signal);
- disable_async_io ();
+ if (non_stop)
+ write_ok (own_buf);
+ else
+ {
+ last_ptid = mywait (minus_one_ptid, &last_status, 0, 1);
+ prepare_resume_reply (own_buf, last_ptid, &last_status);
+ disable_async_io ();
+ }
return;
err:
@@ -1035,19 +1258,32 @@ err:
/* Attach to a new program. Return 1 if successful, 0 if failure. */
int
-handle_v_attach (char *own_buf, char *status, int *signal)
+handle_v_attach (char *own_buf)
{
int pid;
pid = strtol (own_buf + 8, NULL, 16);
- if (pid != 0 && attach_inferior (pid, status, signal) == 0)
+ if (pid != 0 && attach_inferior (pid) == 0)
{
/* Don't report shared library events after attaching, even if
some libraries are preloaded. GDB will always poll the
library list. Avoids the "stopped by shared library event"
notice on the GDB side. */
dlls_changed = 0;
- prepare_resume_reply (own_buf, *status, *signal);
+
+ if (non_stop)
+ {
+ /* In non-stop, we don't send a resume reply. GDB will
+ query for the current thread, so set it. */
+ general_thread = last_ptid;
+
+ /* Stop events will follow up using the normal notification
+ mechanism. */
+ write_ok (own_buf);
+ }
+ else
+ prepare_resume_reply (own_buf, last_ptid, &last_status);
+
return 1;
}
else
@@ -1059,7 +1295,7 @@ handle_v_attach (char *own_buf, char *status, int *signal)
/* Run a new program. Return 1 if successful, 0 if failure. */
static int
-handle_v_run (char *own_buf, char *status, int *signal)
+handle_v_run (char *own_buf)
{
char *p, **pp, *next_p, **new_argv;
int i, new_argc;
@@ -1096,36 +1332,58 @@ handle_v_run (char *own_buf, char *status, int *signal)
if (new_argv[0] == NULL)
{
- /* GDB didn't specify a program to run. Try to use the argv
- from the last run: either from the last vRun with a non-empty
- argv, or from what the user specified if gdbserver was
- started as: `gdbserver :1234 PROG ARGS'. */
-
if (program_argv == NULL)
{
write_enn (own_buf);
return 0;
}
- /* We can reuse the old args. We don't need this then. */
- free (new_argv);
+ new_argv[0] = strdup (program_argv[0]);
+ }
+
+ /* Free the old argv. */
+ if (program_argv)
+ {
+ for (pp = program_argv; *pp != NULL; pp++)
+ free (*pp);
+ free (program_argv);
+ }
+ program_argv = new_argv;
+
+ start_inferior (program_argv);
+ if (last_status.kind == TARGET_WAITKIND_STOPPED)
+ {
+ prepare_resume_reply (own_buf, last_ptid, &last_status);
+
+ /* In non-stop, sending a resume reply doesn't set the general
+ thread, but GDB assumes a vRun sets it (this is so GDB can
+ query which is the main thread of the new inferior. */
+ if (non_stop)
+ general_thread = last_ptid;
+
+ return 1;
}
else
{
- /* Free the old argv. */
- if (program_argv)
- {
- for (pp = program_argv; *pp != NULL; pp++)
- free (*pp);
- free (program_argv);
- }
- program_argv = new_argv;
+ write_enn (own_buf);
+ return 0;
}
+}
+
+/* Attach to a new program. Return 1 if successful, 0 if failure. */
+int
+handle_v_kill (char *own_buf)
+{
+ int pid;
+ char *p = &own_buf[6];
- *signal = start_inferior (program_argv, status);
- if (*status == 'T')
+ pid = strtol (p, NULL, 16);
+ if (pid != 0 && kill_inferior (pid) == 0)
{
- prepare_resume_reply (own_buf, *status, *signal);
+ last_status.kind = TARGET_WAITKIND_SIGNALLED;
+ last_status.value.sig = TARGET_SIGNAL_KILL;
+ discard_queued_stop_replies (pid);
+ write_ok (own_buf);
return 1;
}
else
@@ -1135,23 +1393,45 @@ handle_v_run (char *own_buf, char *status, int *signal)
}
}
+/* Handle a 'vStopped' packet. */
+static void
+handle_v_stopped (char *own_buf)
+{
+ /* If we're waiting for GDB to acknowledge a pending stop reply,
+ consider that done. */
+ if (notif_queue)
+ {
+ struct vstop_notif *head;
+
+ if (remote_debug)
+ fprintf (stderr, "vStopped: acking %s\n",
+ target_pid_to_str (notif_queue->ptid));
+
+ head = notif_queue;
+ notif_queue = notif_queue->next;
+ free (head);
+ }
+
+ /* Push another, or if there are no more left, and OK. */
+ send_next_stop_reply (own_buf);
+}
+
/* Handle all of the extended 'v' packets. */
void
-handle_v_requests (char *own_buf, char *status, int *signal,
- int packet_len, int *new_packet_len)
+handle_v_requests (char *own_buf, int packet_len, int *new_packet_len)
{
if (!disable_packet_vCont)
{
if (strncmp (own_buf, "vCont;", 6) == 0)
{
require_running (own_buf);
- handle_v_cont (own_buf, status, signal);
+ handle_v_cont (own_buf);
return;
}
if (strncmp (own_buf, "vCont?", 6) == 0)
{
- strcpy (own_buf, "vCont;c;C;s;S");
+ strcpy (own_buf, "vCont;c;C;s;S;t");
return;
}
}
@@ -1162,25 +1442,43 @@ handle_v_requests (char *own_buf, char *status, int *signal,
if (strncmp (own_buf, "vAttach;", 8) == 0)
{
- if (target_running ())
+ if (!multi_process && target_running ())
{
fprintf (stderr, "Already debugging a process\n");
write_enn (own_buf);
return;
}
- handle_v_attach (own_buf, status, signal);
+ handle_v_attach (own_buf);
return;
}
if (strncmp (own_buf, "vRun;", 5) == 0)
{
- if (target_running ())
+ if (!multi_process && target_running ())
{
fprintf (stderr, "Already debugging a process\n");
write_enn (own_buf);
return;
}
- handle_v_run (own_buf, status, signal);
+ handle_v_run (own_buf);
+ return;
+ }
+
+ if (strncmp (own_buf, "vKill;", 6) == 0)
+ {
+ if (!target_running ())
+ {
+ fprintf (stderr, "No process to kill\n");
+ write_enn (own_buf);
+ return;
+ }
+ handle_v_kill (own_buf);
+ return;
+ }
+
+ if (strncmp (own_buf, "vStopped", 8) == 0)
+ {
+ handle_v_stopped (own_buf);
return;
}
@@ -1190,34 +1488,109 @@ handle_v_requests (char *own_buf, char *status, int *signal,
return;
}
+/* Resume inferior and wait for another event. In non-stop mode,
+ don't really wait here, but return immediatelly to the event
+ loop. */
void
-myresume (char *own_buf, int step, int *signalp, char *statusp)
+proceed (char *own_buf, int step, int siggnal)
{
struct thread_resume resume_info[2];
int n = 0;
- int sig = *signalp;
+ int valid_cont_thread;
set_desired_inferior (0);
- if (step || sig || (cont_thread != 0 && cont_thread != -1))
+ valid_cont_thread = (!ptid_equal (cont_thread, null_ptid)
+ && !ptid_equal (cont_thread, minus_one_ptid));
+
+ if (step || siggnal || valid_cont_thread)
{
resume_info[0].thread
= ((struct inferior_list_entry *) current_inferior)->id;
- resume_info[0].step = step;
- resume_info[0].sig = sig;
- resume_info[0].leave_stopped = 0;
+ if (step)
+ resume_info[0].kind = rk_step;
+ else
+ resume_info[0].kind = rk_continue;
+ resume_info[0].sig = siggnal;
n++;
}
- resume_info[n].thread = -1;
- resume_info[n].step = 0;
- resume_info[n].sig = 0;
- resume_info[n].leave_stopped = (cont_thread != 0 && cont_thread != -1);
- enable_async_io ();
- (*the_target->resume) (resume_info);
- *signalp = mywait (statusp, 1);
- prepare_resume_reply (own_buf, *statusp, *signalp);
- disable_async_io ();
+ if (!valid_cont_thread)
+ {
+ resume_info[n].thread = minus_one_ptid;
+ resume_info[n].kind = rk_continue;
+ resume_info[n].sig = 0;
+ }
+
+ 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);
+ prepare_resume_reply (own_buf, last_ptid, &last_status);
+ disable_async_io ();
+ }
+}
+
+/* Callback for for_each_inferior. Make a new stop reply for each
+ stopped thread. */
+static int
+queue_stop_reply_callback (struct inferior_list_entry *entry, void *arg)
+{
+ int pid = * (int *) arg;
+
+ if (pid == -1
+ || ptid_get_pid (entry->id) == pid)
+ {
+ 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);
+ }
+
+ return 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;
+
+ /* 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
+ thread we find. */
+
+ if (non_stop)
+ {
+ int pid = -1;
+ discard_queued_stop_replies (pid);
+ find_inferior (&all_threads, queue_stop_reply_callback, &pid);
+
+ /* The first is sent immediatly. OK is sent if there is no
+ stopped thread, which is the same handling of the vStopped
+ packet (by design). */
+ send_next_stop_reply (own_buf);
+ }
+ else
+ {
+ if (all_threads.head)
+ prepare_resume_reply (own_buf,
+ all_threads.head->id, &status);
+ else
+ strcpy (own_buf, "W00");
+ }
}
static void
@@ -1241,7 +1614,8 @@ gdbserver_usage (FILE *stream)
"HOST:PORT to listen for a TCP connection.\n"
"\n"
"Options:\n"
- " --debug\t\tEnable debugging output.\n"
+ " --debug\t\tEnable general debugging output.\n"
+ " --remote-debug\tEnable remote protocol debugging output.\n"
" --version\t\tDisplay version information and exit.\n"
" --wrapper WRAPPER --\tRun WRAPPER to start new programs.\n");
if (REPORT_BUGS_TO[0] && stream == stdout)
@@ -1268,15 +1642,51 @@ gdbserver_show_disableable (FILE *stream)
break; \
}
+static int
+first_thread_of (struct inferior_list_entry *entry, void *args)
+{
+ int pid = * (int *) args;
+
+ if (ptid_get_pid (entry->id) == pid)
+ return 1;
+
+ return 0;
+}
+
+static void
+kill_inferior_callback (struct inferior_list_entry *entry)
+{
+ struct process_info *process = (struct process_info *) entry;
+ int pid = ptid_get_pid (process->head.id);
+
+ kill_inferior (pid);
+ discard_queued_stop_replies (pid);
+}
+
+static void
+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);
+
+ if (process->attached)
+ detach_inferior (pid);
+ else
+ kill_inferior (pid);
+
+ discard_queued_stop_replies (pid);
+}
+
+static void
+join_inferiors_callback (struct inferior_list_entry *entry)
+{
+ struct process_info *process = (struct process_info *) entry;
+ join_inferior (ptid_get_pid (process->head.id));
+}
+
int
main (int argc, char *argv[])
{
- char ch, status, *own_buf;
- unsigned char *mem_buf;
- int i = 0;
- int signal;
- unsigned int len;
- CORE_ADDR mem_addr;
int bad_attach;
int pid;
char *arg_end, *port;
@@ -1320,6 +1730,8 @@ main (int argc, char *argv[])
}
else if (strcmp (*next_arg, "--debug") == 0)
debug_threads = 1;
+ else if (strcmp (*next_arg, "--remote-debug") == 0)
+ remote_debug = 1;
else if (strcmp (*next_arg, "--disable-packet") == 0)
{
gdbserver_show_disableable (stdout);
@@ -1409,6 +1821,7 @@ main (int argc, char *argv[])
exit (1);
}
+ initialize_inferiors ();
initialize_async_io ();
initialize_low ();
@@ -1426,7 +1839,7 @@ main (int argc, char *argv[])
program_argv[i] = NULL;
/* Wait till we are at first instruction in program. */
- signal = start_inferior (program_argv, &status);
+ start_inferior (program_argv);
/* We are now (hopefully) stopped at the first instruction of
the target process. This assumes that the target process was
@@ -1434,15 +1847,16 @@ main (int argc, char *argv[])
}
else if (pid != 0)
{
- if (attach_inferior (pid, &status, &signal) == -1)
+ if (attach_inferior (pid) == -1)
error ("Attaching not supported on this target");
/* Otherwise succeeded. */
}
else
{
- status = 'W';
- signal = 0;
+ last_status.kind = TARGET_WAITKIND_EXITED;
+ last_status.value.integer = 0;
+ last_ptid = minus_one_ptid;
}
/* Don't report shared library events on the initial connection,
@@ -1452,12 +1866,14 @@ main (int argc, char *argv[])
if (setjmp (toplevel))
{
- fprintf (stderr, "Killing inferior\n");
- kill_inferior ();
+ fprintf (stderr, "Killing all inferiors\n");
+ for_each_inferior (&all_processes,
+ kill_inferior_callback);
exit (1);
}
- if (status == 'W' || status == 'X')
+ if (last_status.kind == TARGET_WAITKIND_EXITED
+ || last_status.kind == TARGET_WAITKIND_SIGNALLED)
was_running = 0;
else
was_running = 1;
@@ -1471,9 +1887,11 @@ main (int argc, char *argv[])
while (1)
{
noack_mode = 0;
+ multi_process = 0;
+ non_stop = 0;
+
remote_open (port);
- restart:
if (setjmp (toplevel) != 0)
{
/* An error occurred. */
@@ -1484,8 +1902,49 @@ main (int argc, char *argv[])
}
}
+ /* Wait for events. This will return when all event sources are
+ removed from the event loop. */
+ start_event_loop ();
+
+ /* If an exit was requested (using the "monitor exit" command),
+ terminate now. The only other way to get here is for
+ getpkt to fail; close the connection and reopen it at the
+ top of the loop. */
+
+ if (exit_requested)
+ {
+ for_each_inferior (&all_processes,
+ detach_or_kill_inferior_callback);
+ exit (0);
+ }
+ else
+ fprintf (stderr, "Remote side has terminated connection. "
+ "GDBserver will reopen the connection.\n");
+ }
+}
+
+/* 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
+ getpkt call. */
+static void
+process_serial_event (void)
+{
+ char ch;
+ int i = 0;
+ int signal;
+ unsigned int len;
+ CORE_ADDR mem_addr;
+ int pid;
+
+ /* Used to decide when gdbserver should exit in
+ multi-mode/remote. */
+ static int have_ran = 0;
+
+ if (!have_ran)
+ have_ran = target_running ();
+
disable_async_io ();
- while (!exit_requested)
{
unsigned char sig;
int packet_len;
@@ -1494,7 +1953,11 @@ main (int argc, char *argv[])
response_needed = 0;
packet_len = getpkt (own_buf);
if (packet_len <= 0)
- break;
+ {
+ target_async (0);
+ remote_close ();
+ return;
+ }
response_needed = 1;
i = 0;
@@ -1509,29 +1972,46 @@ main (int argc, char *argv[])
break;
case 'D':
require_running (own_buf);
- fprintf (stderr, "Detaching from inferior\n");
- if (detach_inferior () != 0)
+
+ if (multi_process)
+ {
+ i++; /* skip ';' */
+ pid = strtol (&own_buf[i], NULL, 16);
+ }
+ else
+ pid = ptid_get_pid (((struct inferior_list_entry *)current_inferior)->id);
+
+ fprintf (stderr, "Detaching from process %d\n", pid);
+ if (detach_inferior (pid) != 0)
write_enn (own_buf);
else
{
+ discard_queued_stop_replies (pid);
write_ok (own_buf);
if (extended_protocol)
{
/* Treat this like a normal program exit. */
- signal = 0;
- status = 'W';
+ last_status.kind = TARGET_WAITKIND_EXITED;
+ last_status.value.integer = 0;
+ last_ptid = pid_to_ptid (pid);
+
+ current_inferior = NULL;
}
else
{
putpkt (own_buf);
remote_close ();
- /* If we are attached, then we can exit. Otherwise, we
- need to hang around doing nothing, until the child
- is gone. */
- if (!attached)
- join_inferior ();
+ /* Usually, if we are attached, then we can
+ exit. Otherwise, we need to hang around
+ doing nothing, until the child is gone. The
+ decision to join or not is left to the
+ target, as some targets may have to join even
+ if attached (e.g., Windows versions without
+ DebugSetProcessKillOnExit). */
+ for_each_inferior (&all_processes,
+ join_inferiors_callback);
exit (0);
}
@@ -1542,21 +2022,43 @@ main (int argc, char *argv[])
write_ok (own_buf);
break;
case '?':
- prepare_resume_reply (own_buf, status, signal);
+ handle_status (own_buf);
break;
case 'H':
if (own_buf[1] == 'c' || own_buf[1] == 'g' || own_buf[1] == 's')
{
- unsigned long gdb_id, thread_id;
+ ptid_t gdb_id, thread_id;
+ int pid;
require_running (own_buf);
- gdb_id = strtoul (&own_buf[2], NULL, 16);
- if (gdb_id == 0 || gdb_id == -1)
- thread_id = gdb_id;
+
+ gdb_id = read_ptid (&own_buf[2], NULL);
+
+ pid = ptid_get_pid (gdb_id);
+
+ if (ptid_equal (gdb_id, null_ptid)
+ || ptid_equal (gdb_id, minus_one_ptid))
+ thread_id = null_ptid;
+ else if (pid != 0
+ && ptid_equal (pid_to_ptid (pid),
+ gdb_id))
+ {
+ struct thread_info *thread =
+ (struct thread_info *) find_inferior (&all_threads,
+ first_thread_of,
+ &pid);
+ if (!thread)
+ {
+ write_enn (own_buf);
+ break;
+ }
+
+ thread_id = ((struct inferior_list_entry *)thread)->id;
+ }
else
{
thread_id = gdb_id_to_thread_id (gdb_id);
- if (thread_id == 0)
+ if (ptid_equal (thread_id, null_ptid))
{
write_enn (own_buf);
break;
@@ -1565,6 +2067,19 @@ main (int argc, char *argv[])
if (own_buf[1] == 'g')
{
+ if (ptid_equal (thread_id, null_ptid))
+ {
+ /* GDB is telling us to choose any thread.
+ Check if the currently selected thread is
+ still valid. If it is not, select the
+ first available. */
+ struct thread_info *thread =
+ (struct thread_info *) find_inferior_id (&all_threads,
+ general_thread);
+ if (thread == NULL)
+ thread_id = all_threads.head->id;
+ }
+
general_thread = thread_id;
set_desired_inferior (1);
}
@@ -1611,9 +2126,11 @@ main (int argc, char *argv[])
break;
case 'X':
require_running (own_buf);
+
+ /* If len == 0, GDB is probing for packet support. */
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)
+ || (len > 0 && write_inferior_memory (mem_addr, mem_buf, len) != 0))
write_enn (own_buf);
else
write_ok (own_buf);
@@ -1625,7 +2142,7 @@ main (int argc, char *argv[])
signal = target_signal_to_host (sig);
else
signal = 0;
- myresume (own_buf, 0, &signal, &status);
+ proceed (own_buf, 0, signal);
break;
case 'S':
require_running (own_buf);
@@ -1634,17 +2151,17 @@ main (int argc, char *argv[])
signal = target_signal_to_host (sig);
else
signal = 0;
- myresume (own_buf, 1, &signal, &status);
+ proceed (own_buf, 1, signal);
break;
case 'c':
require_running (own_buf);
signal = 0;
- myresume (own_buf, 0, &signal, &status);
+ proceed (own_buf, 0, signal);
break;
case 's':
require_running (own_buf);
signal = 0;
- myresume (own_buf, 1, &signal, &status);
+ proceed (own_buf, 1, signal);
break;
case 'Z':
{
@@ -1713,20 +2230,19 @@ main (int argc, char *argv[])
if (!target_running ())
/* The packet we received doesn't make sense - but we
can't reply to it, either. */
- goto restart;
+ return;
- fprintf (stderr, "Killing inferior\n");
- kill_inferior ();
+ fprintf (stderr, "Killing all inferiors\n");
+ for_each_inferior (&all_processes, kill_inferior_callback);
/* When using the extended protocol, we wait with no
program running. The traditional protocol will exit
instead. */
if (extended_protocol)
{
- status = 'X';
- signal = TARGET_SIGNAL_KILL;
- was_running = 0;
- goto restart;
+ last_status.kind = TARGET_WAITKIND_EXITED;
+ last_status.value.sig = TARGET_SIGNAL_KILL;
+ return;
}
else
{
@@ -1735,12 +2251,13 @@ main (int argc, char *argv[])
}
case 'T':
{
- unsigned long gdb_id, thread_id;
+ ptid_t gdb_id, thread_id;
require_running (own_buf);
- gdb_id = strtoul (&own_buf[1], NULL, 16);
+
+ gdb_id = read_ptid (&own_buf[1], NULL);
thread_id = gdb_id_to_thread_id (gdb_id);
- if (thread_id == 0)
+ if (ptid_equal (thread_id, null_ptid))
{
write_enn (own_buf);
break;
@@ -1760,18 +2277,19 @@ main (int argc, char *argv[])
if (extended_protocol)
{
if (target_running ())
- kill_inferior ();
+ for_each_inferior (&all_processes,
+ kill_inferior_callback);
fprintf (stderr, "GDBserver restarting\n");
/* Wait till we are at 1st instruction in prog. */
if (program_argv != NULL)
- signal = start_inferior (program_argv, &status);
+ start_inferior (program_argv);
else
{
- status = 'X';
- signal = TARGET_SIGNAL_KILL;
+ last_status.kind = TARGET_WAITKIND_EXITED;
+ last_status.value.sig = TARGET_SIGNAL_KILL;
}
- goto restart;
+ return;
}
else
{
@@ -1783,8 +2301,7 @@ main (int argc, char *argv[])
}
case 'v':
/* Extended (long) request. */
- handle_v_requests (own_buf, &status, &signal,
- packet_len, &new_packet_len);
+ handle_v_requests (own_buf, packet_len, &new_packet_len);
break;
default:
@@ -1802,50 +2319,52 @@ main (int argc, char *argv[])
response_needed = 0;
- if (was_running && (status == 'W' || status == 'X'))
+ if (!extended_protocol && have_ran && !target_running ())
{
- was_running = 0;
-
- if (status == 'W')
- fprintf (stderr,
- "\nChild exited with status %d\n", signal);
- if (status == 'X')
- fprintf (stderr, "\nChild terminated with signal = 0x%x (%s)\n",
- target_signal_to_host (signal),
- target_signal_to_name (signal));
-
- if (extended_protocol)
- goto restart;
- else
+ /* In non-stop, defer exiting until GDB had a chance to
+ query the whole vStopped list (until it gets an
+ OK). */
+ if (!notif_queue)
{
fprintf (stderr, "GDBserver exiting\n");
exit (0);
}
}
-
- if (status != 'W' && status != 'X')
- was_running = 1;
}
+}
- /* If an exit was requested (using the "monitor exit" command),
- terminate now. The only other way to get here is for
- getpkt to fail; close the connection and reopen it at the
- top of the loop. */
+/* Event-loop callback for serial events. */
+void
+handle_serial_event (int err, gdb_client_data client_data)
+{
+ if (debug_threads)
+ fprintf (stderr, "handling possible serial event\n");
- if (exit_requested)
- {
- remote_close ();
- if (attached && target_running ())
- detach_inferior ();
- else if (target_running ())
- kill_inferior ();
- exit (0);
- }
- else
- {
- fprintf (stderr, "Remote side has terminated connection. "
- "GDBserver will reopen the connection.\n");
- remote_close ();
- }
- }
+ /* Really handle it. */
+ process_serial_event ();
+
+ /* Be sure to not change the selected inferior behind GDB's back.
+ Important in the non-stop mode asynchronous protocol. */
+ set_desired_inferior (1);
+}
+
+/* Event-loop callback for the target events. */
+void
+handle_target_event (int err, gdb_client_data client_data)
+{
+ if (debug_threads)
+ fprintf (stderr, "handling possible target event\n");
+
+ last_ptid = mywait (minus_one_ptid, &last_status,
+ TARGET_WNOHANG, 1);
+
+ if (last_status.kind == TARGET_WAITKIND_IGNORE)
+ return; /* Nothing interesting found. */
+
+ /* 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);
}