From 8336d594d5107cb86bf1b569962bf1496e2c7c30 Mon Sep 17 00:00:00 2001 From: Pedro Alves Date: Sun, 11 Apr 2010 16:33:56 +0000 Subject: [PATCH] GDBserver disconnected tracing support. * linux-low.c (linux_remove_process): Delete. (add_lwp): Don't set last_resume_kind here. (linux_kill): Use `mourn'. (linux_detach): Use `thread_db_detach', and `mourn'. (linux_mourn): New. (linux_attach_lwp_1): Adjust comment. (linux_attach): last_resume_kind moved the thread_info; adjust. (status_pending_p_callback): Adjust. (linux_wait_for_event_1): Adjust. (count_events_callback, select_singlestep_lwp_callback) (select_event_lwp_callback, cancel_breakpoints_callback) (db_wants_lwp_stopped, linux_wait_1, need_step_over_p) (proceed_one_lwp): Adjust. (linux_async): Add debug output. (linux_thread_stopped): New. (linux_pause_all): New. (linux_target_ops): Install linux_mourn, linux_thread_stopped and linux_pause_all. * linux-low.h (struct lwp_info): Delete last_resume_kind field. (thread_db_free): Delete declaration. (thread_db_detach, thread_db_mourn): Declare. * thread-db.c (thread_db_init): Use thread_db_mourn. (thread_db_free): Delete, split in two. (disable_thread_event_reporting): New. (thread_db_detach): New. (thread_db_mourn): New. * server.h (struct thread_info) : New field. : Add comment. : New field. (handler_func): Change return type to int. (handle_serial_event, handle_target_event): Ditto. (gdb_connected): Declare. (tracing): Delete. (disconnected_tracing): Declare. (stop_tracing): Declare. * server.c (handle_query) : Report support for disconnected tracing. (queue_stop_reply_callback): Account for running threads. (gdb_wants_thread_stopped): New. (gdb_wants_all_threads_stopped): New. (gdb_reattached_process): New. (handle_status): Clear the `gdb_detached' flag of all processes. In all-stop, stop all threads. (main): Be sure to leave tfind mode. Handle disconnected tracing. (process_serial_event): If the remote connection breaks, or if an exit was forced with "monitor exit", force an event loop exit. Handle disconnected tracing on detach. (handle_serial_event): Adjust. (handle_target_event): If GDB isn't connected, forward events back to the inferior, unless the last process exited, in which case, exit gdbserver. Adjust interface. * remote-utils.c (remote_open): Don't block in accept. Instead register an event loop source on the listen socket file descriptor. Refactor bits into ... (listen_desc): ... this new global. (gdb_connected): ... this new function. (enable_async_notification): ... this new function. (handle_accept_event): ... this new function. (remote_close): Clear remote_desc. * inferiors.c (add_thread): Set the new thread's last_resume_kind. * target.h (struct target_ops) : New fields. (mourn_inferior): Define. (target_process_qsupported): Avoid the dangling else problem. (thread_stopped): Define. (pause_all): Define. (target_waitstatus_to_string): Declare. * target.c (target_waitstatus_to_string): New. * tracepoint.c (tracing): Make extern. (disconnected_tracing): New. (stop_tracing): Make extern. Handle tracing stops due to GDB disconnecting. (cmd_qtdisconnected): New. (cmd_qtstatus): Report disconnected tracing status in trace reply. (handle_tracepoint_general_set): Handle QTDisconnected. * event-loop.c (event_handler_func): Change return type to int. (process_event): Bail out if the event handler wants the event loop to stop. (handle_file_event): Ditto. (start_event_loop): Bail out if the event handler wants the event loop to stop. * nto-low.c (nto_target_ops): Adjust. * spu-low.c (spu_wait): Don't remove the process here. (spu_target_ops): Adjust. * win32-low.c (win32_wait): Don't remove the process here. (win32_target_ops): Adjust. --- gdb/gdbserver/ChangeLog | 99 ++++++++++++++ gdb/gdbserver/event-loop.c | 23 +++- gdb/gdbserver/inferiors.c | 1 + gdb/gdbserver/linux-low.c | 142 +++++++++++--------- gdb/gdbserver/linux-low.h | 6 +- gdb/gdbserver/nto-low.c | 1 + gdb/gdbserver/remote-utils.c | 143 +++++++++++++------- gdb/gdbserver/server.c | 249 ++++++++++++++++++++++++++++++----- gdb/gdbserver/server.h | 22 +++- gdb/gdbserver/spu-low.c | 3 +- gdb/gdbserver/target.c | 42 ++++++ gdb/gdbserver/target.h | 34 ++++- gdb/gdbserver/thread-db.c | 54 +++++--- gdb/gdbserver/tracepoint.c | 48 ++++++- gdb/gdbserver/win32-low.c | 4 +- 15 files changed, 686 insertions(+), 185 deletions(-) diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog index 6e29310ebb..86caf1e763 100644 --- a/gdb/gdbserver/ChangeLog +++ b/gdb/gdbserver/ChangeLog @@ -1,3 +1,102 @@ +2010-04-11 Pedro Alves + + GDBserver disconnected tracing support. + + * linux-low.c (linux_remove_process): Delete. + (add_lwp): Don't set last_resume_kind here. + (linux_kill): Use `mourn'. + (linux_detach): Use `thread_db_detach', and `mourn'. + (linux_mourn): New. + (linux_attach_lwp_1): Adjust comment. + (linux_attach): last_resume_kind moved the thread_info; adjust. + (status_pending_p_callback): Adjust. + (linux_wait_for_event_1): Adjust. + (count_events_callback, select_singlestep_lwp_callback) + (select_event_lwp_callback, cancel_breakpoints_callback) + (db_wants_lwp_stopped, linux_wait_1, need_step_over_p) + (proceed_one_lwp): Adjust. + (linux_async): Add debug output. + (linux_thread_stopped): New. + (linux_pause_all): New. + (linux_target_ops): Install linux_mourn, linux_thread_stopped and + linux_pause_all. + * linux-low.h (struct lwp_info): Delete last_resume_kind field. + (thread_db_free): Delete declaration. + (thread_db_detach, thread_db_mourn): Declare. + * thread-db.c (thread_db_init): Use thread_db_mourn. + (thread_db_free): Delete, split in two. + (disable_thread_event_reporting): New. + (thread_db_detach): New. + (thread_db_mourn): New. + + * server.h (struct thread_info) : New field. + : Add comment. + : New field. + (handler_func): Change return type to int. + (handle_serial_event, handle_target_event): Ditto. + (gdb_connected): Declare. + (tracing): Delete. + (disconnected_tracing): Declare. + (stop_tracing): Declare. + + * server.c (handle_query) : Report support for + disconnected tracing. + (queue_stop_reply_callback): Account for running threads. + (gdb_wants_thread_stopped): New. + (gdb_wants_all_threads_stopped): New. + (gdb_reattached_process): New. + (handle_status): Clear the `gdb_detached' flag of all processes. + In all-stop, stop all threads. + (main): Be sure to leave tfind mode. Handle disconnected tracing. + (process_serial_event): If the remote connection breaks, or if an + exit was forced with "monitor exit", force an event loop exit. + Handle disconnected tracing on detach. + (handle_serial_event): Adjust. + (handle_target_event): If GDB isn't connected, forward events back + to the inferior, unless the last process exited, in which case, + exit gdbserver. Adjust interface. + + * remote-utils.c (remote_open): Don't block in accept. Instead + register an event loop source on the listen socket file + descriptor. Refactor bits into ... + (listen_desc): ... this new global. + (gdb_connected): ... this new function. + (enable_async_notification): ... this new function. + (handle_accept_event): ... this new function. + (remote_close): Clear remote_desc. + + * inferiors.c (add_thread): Set the new thread's last_resume_kind. + + * target.h (struct target_ops) : + New fields. + (mourn_inferior): Define. + (target_process_qsupported): Avoid the dangling else problem. + (thread_stopped): Define. + (pause_all): Define. + (target_waitstatus_to_string): Declare. + * target.c (target_waitstatus_to_string): New. + + * tracepoint.c (tracing): Make extern. + (disconnected_tracing): New. + (stop_tracing): Make extern. Handle tracing stops due to GDB + disconnecting. + (cmd_qtdisconnected): New. + (cmd_qtstatus): Report disconnected tracing status in trace reply. + (handle_tracepoint_general_set): Handle QTDisconnected. + + * event-loop.c (event_handler_func): Change return type to int. + (process_event): Bail out if the event handler wants the event + loop to stop. + (handle_file_event): Ditto. + (start_event_loop): Bail out if the event handler wants the event + loop to stop. + + * nto-low.c (nto_target_ops): Adjust. + * spu-low.c (spu_wait): Don't remove the process here. + (spu_target_ops): Adjust. + * win32-low.c (win32_wait): Don't remove the process here. + (win32_target_ops): Adjust. + 2010-04-11 Pedro Alves * regcache.c (realloc_register_cache): Invalidate inferior's diff --git a/gdb/gdbserver/event-loop.c b/gdb/gdbserver/event-loop.c index 7482e1cf5a..6471772e16 100644 --- a/gdb/gdbserver/event-loop.c +++ b/gdb/gdbserver/event-loop.c @@ -39,7 +39,7 @@ #endif typedef struct gdb_event gdb_event; -typedef void (event_handler_func) (int); +typedef int (event_handler_func) (int); /* Tell create_file_handler what events we are interested in. */ @@ -211,7 +211,8 @@ process_event (void) free (event_ptr); /* Now call the procedure associated with the event. */ - (*proc) (fd); + if ((*proc) (fd)) + return -1; return 1; } @@ -347,7 +348,7 @@ delete_file_handler (int fd) through event_ptr->proc. EVENT_FILE_DESC is file descriptor of the event in the front of the event queue. */ -static void +static int handle_file_event (int event_file_desc) { file_handler *file_ptr; @@ -378,10 +379,16 @@ handle_file_event (int event_file_desc) /* If there was a match, then call the handler. */ if (mask != 0) - (*file_ptr->proc) (file_ptr->error, file_ptr->client_data); + { + if ((*file_ptr->proc) (file_ptr->error, + file_ptr->client_data) < 0) + return -1; + } break; } } + + return 0; } /* Create a file event, to be enqueued in the event queue for @@ -491,7 +498,13 @@ start_event_loop (void) while (1) { /* Any events already waiting in the queue? */ - if (process_event ()) + int res = process_event (); + + /* Did the event handler want the event loop to stop? */ + if (res == -1) + return; + + if (res) continue; /* Wait for a new event. If wait_for_event returns -1, we diff --git a/gdb/gdbserver/inferiors.c b/gdb/gdbserver/inferiors.c index 8e3f1de6bb..787b018f75 100644 --- a/gdb/gdbserver/inferiors.c +++ b/gdb/gdbserver/inferiors.c @@ -171,6 +171,7 @@ add_thread (ptid_t thread_id, void *target_data) memset (new_thread, 0, sizeof (*new_thread)); new_thread->entry.id = thread_id; + new_thread->last_resume_kind = resume_continue; new_thread->last_status.kind = TARGET_WAITKIND_IGNORE; add_inferior_to_list (&all_threads, & new_thread->entry); diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c index 3e37722e78..fcdcc45fdb 100644 --- a/gdb/gdbserver/linux-low.c +++ b/gdb/gdbserver/linux-low.c @@ -287,19 +287,6 @@ linux_add_process (int pid, int attached) return proc; } -/* Remove a process from the common process list, - also freeing all private data. */ - -static void -linux_remove_process (struct process_info *process) -{ - struct process_info_private *priv = process->private; - - free (priv->arch_private); - free (priv); - remove_process (process); -} - /* Wrapper function for waitpid which handles EINTR, and emulates __WALL for systems where that is not available. */ @@ -534,8 +521,6 @@ add_lwp (ptid_t ptid) lwp->head.id = ptid; - lwp->last_resume_kind = resume_continue; - if (the_low_target.new_thread != NULL) lwp->arch_private = the_low_target.new_thread (); @@ -644,7 +629,7 @@ linux_attach_lwp_1 (unsigned long lwpid, int initial) of a new thread that is being created. In this case we should ignore that SIGSTOP and resume the process. This is handled below by setting stop_expected = 1, - and the fact that add_lwp sets last_resume_kind == + and the fact that add_thread sets last_resume_kind == resume_continue. 2) This is the first thread (the process thread), and we're attaching @@ -680,19 +665,17 @@ linux_attach_lwp (unsigned long lwpid) int linux_attach (unsigned long pid) { - struct lwp_info *lwp; - linux_attach_lwp_1 (pid, 1); - linux_add_process (pid, 1); if (!non_stop) { - /* Don't ignore the initial SIGSTOP if we just attached to this - process. It will be collected by wait shortly. */ - lwp = (struct lwp_info *) find_inferior_id (&all_lwps, - ptid_build (pid, pid, 0)); - lwp->last_resume_kind = resume_stop; + struct thread_info *thread; + + /* Don't ignore the initial SIGSTOP if we just attached to this + process. It will be collected by wait shortly. */ + thread = find_thread_ptid (ptid_build (pid, pid, 0)); + thread->last_resume_kind = resume_stop; } return 0; @@ -808,11 +791,9 @@ linux_kill (int pid) lwpid = linux_wait_for_event (lwp->head.id, &wstat, __WALL); } while (lwpid > 0 && WIFSTOPPED (wstat)); -#ifdef USE_THREAD_DB - thread_db_free (process, 0); -#endif delete_lwp (lwp); - linux_remove_process (process); + + the_target->mourn (process); return 0; } @@ -893,7 +874,7 @@ linux_detach (int pid) return -1; #ifdef USE_THREAD_DB - thread_db_free (process, 1); + thread_db_detach (process); #endif current_inferior = @@ -901,10 +882,27 @@ linux_detach (int pid) delete_all_breakpoints (); find_inferior (&all_threads, linux_detach_one_lwp, &pid); - linux_remove_process (process); + + the_target->mourn (process); return 0; } +static void +linux_mourn (struct process_info *process) +{ + struct process_info_private *priv; + +#ifdef USE_THREAD_DB + thread_db_mourn (process); +#endif + + /* Freeing all private data. */ + priv = process->private; + free (priv->arch_private); + free (priv); + process->private = NULL; +} + static void linux_join (int pid) { @@ -955,7 +953,7 @@ status_pending_p_callback (struct inferior_list_entry *entry, void *arg) /* If we got a `vCont;t', but we haven't reported a stop yet, do report any status pending the LWP may have. */ - if (lwp->last_resume_kind == resume_stop + if (thread->last_resume_kind == resume_stop && thread->last_status.kind == TARGET_WAITKIND_STOPPED) return 0; @@ -1377,7 +1375,7 @@ linux_wait_for_event_1 (ptid_t ptid, int *wstat, int options) fprintf (stderr, "Expected stop.\n"); event_child->stop_expected = 0; - should_stop = (event_child->last_resume_kind == resume_stop + should_stop = (current_inferior->last_resume_kind == resume_stop || stopping_threads); if (!should_stop) @@ -1442,14 +1440,15 @@ static int count_events_callback (struct inferior_list_entry *entry, void *data) { struct lwp_info *lp = (struct lwp_info *) entry; + struct thread_info *thread = get_lwp_thread (lp); int *count = data; gdb_assert (count != NULL); /* Count only resumed LWPs that have a SIGTRAP event pending that should be reported to GDB. */ - if (get_lwp_thread (lp)->last_status.kind == TARGET_WAITKIND_IGNORE - && lp->last_resume_kind != resume_stop + if (thread->last_status.kind == TARGET_WAITKIND_IGNORE + && thread->last_resume_kind != resume_stop && lp->status_pending_p && WIFSTOPPED (lp->status_pending) && WSTOPSIG (lp->status_pending) == SIGTRAP @@ -1465,9 +1464,10 @@ static int select_singlestep_lwp_callback (struct inferior_list_entry *entry, void *data) { struct lwp_info *lp = (struct lwp_info *) entry; + struct thread_info *thread = get_lwp_thread (lp); - if (get_lwp_thread (lp)->last_status.kind == TARGET_WAITKIND_IGNORE - && lp->last_resume_kind == resume_step + if (thread->last_status.kind == TARGET_WAITKIND_IGNORE + && thread->last_resume_kind == resume_step && lp->status_pending_p) return 1; else @@ -1481,13 +1481,14 @@ static int select_event_lwp_callback (struct inferior_list_entry *entry, void *data) { struct lwp_info *lp = (struct lwp_info *) entry; + struct thread_info *thread = get_lwp_thread (lp); int *selector = data; gdb_assert (selector != NULL); /* Select only resumed LWPs that have a SIGTRAP event pending. */ - if (lp->last_resume_kind != resume_stop - && get_lwp_thread (lp)->last_status.kind == TARGET_WAITKIND_IGNORE + if (thread->last_resume_kind != resume_stop + && thread->last_status.kind == TARGET_WAITKIND_IGNORE && lp->status_pending_p && WIFSTOPPED (lp->status_pending) && WSTOPSIG (lp->status_pending) == SIGTRAP @@ -1502,6 +1503,7 @@ static int cancel_breakpoints_callback (struct inferior_list_entry *entry, void *data) { struct lwp_info *lp = (struct lwp_info *) entry; + struct thread_info *thread = get_lwp_thread (lp); struct lwp_info *event_lp = data; /* Leave the LWP that has been elected to receive a SIGTRAP alone. */ @@ -1519,8 +1521,8 @@ cancel_breakpoints_callback (struct inferior_list_entry *entry, void *data) delete or disable the breakpoint, but the LWP will have already tripped on it. */ - if (lp->last_resume_kind != resume_stop - && get_lwp_thread (lp)->last_status.kind == TARGET_WAITKIND_IGNORE + if (thread->last_resume_kind != resume_stop + && thread->last_status.kind == TARGET_WAITKIND_IGNORE && lp->status_pending_p && WIFSTOPPED (lp->status_pending) && WSTOPSIG (lp->status_pending) == SIGTRAP @@ -1597,7 +1599,7 @@ gdb_wants_lwp_stopped (struct inferior_list_entry *entry) thread->last_status.kind = TARGET_WAITKIND_STOPPED; thread->last_status.value.sig = TARGET_SIGNAL_0; - lwp->last_resume_kind = resume_stop; + thread->last_resume_kind = resume_stop; } /* Set all LWP's states as "want-stopped". */ @@ -1691,14 +1693,7 @@ retry: { if (WIFEXITED (w) || WIFSIGNALED (w)) { - int pid = pid_of (event_child); - struct process_info *process = find_process_pid (pid); - -#ifdef USE_THREAD_DB - thread_db_free (process, 0); -#endif delete_lwp (event_child); - linux_remove_process (process); current_inferior = NULL; @@ -1800,7 +1795,7 @@ retry: breakpoint and still reporting the event to GDB. If we don't, we're out of luck, GDB won't see the breakpoint hit. */ report_to_gdb = (!maybe_internal_trap - || event_child->last_resume_kind == resume_step + || current_inferior->last_resume_kind == resume_step || event_child->stopped_by_watchpoint || (!step_over_finished && !bp_explains_trap && !trace_event) || gdb_breakpoint_here (event_child->stop_pc)); @@ -1843,7 +1838,7 @@ retry: if (debug_threads) { - if (event_child->last_resume_kind == resume_step) + if (current_inferior->last_resume_kind == resume_step) fprintf (stderr, "GDB wanted to single-step, reporting event.\n"); if (event_child->stopped_by_watchpoint) fprintf (stderr, "Stopped by watchpoint.\n"); @@ -1895,14 +1890,16 @@ retry: /* Do this before the gdb_wants_all_stopped calls below, since they always set last_resume_kind to resume_stop. */ - if (event_child->last_resume_kind == resume_stop && WSTOPSIG (w) == SIGSTOP) + if (current_inferior->last_resume_kind == resume_stop + && WSTOPSIG (w) == SIGSTOP) { /* A thread that has been requested to stop by GDB with vCont;t, and it stopped cleanly, so report as SIG0. The use of SIGSTOP is an implementation detail. */ ourstatus->value.sig = TARGET_SIGNAL_0; } - else if (event_child->last_resume_kind == resume_stop && WSTOPSIG (w) != SIGSTOP) + else if (current_inferior->last_resume_kind == resume_stop + && WSTOPSIG (w) != SIGSTOP) { /* A thread that has been requested to stop by GDB with vCont;t, but, it stopped for other reasons. */ @@ -2361,7 +2358,7 @@ linux_set_resume_request (struct inferior_list_entry *entry, void *arg) && (ptid_get_pid (ptid) == pid_of (lwp)))) { if (r->resume[ndx].kind == resume_stop - && lwp->last_resume_kind == resume_stop) + && thread->last_resume_kind == resume_stop) { if (debug_threads) fprintf (stderr, "already %s LWP %ld at GDB's request\n", @@ -2374,7 +2371,7 @@ linux_set_resume_request (struct inferior_list_entry *entry, void *arg) } lwp->resume = &r->resume[ndx]; - lwp->last_resume_kind = lwp->resume->kind; + thread->last_resume_kind = lwp->resume->kind; return 0; } } @@ -2412,6 +2409,7 @@ static int need_step_over_p (struct inferior_list_entry *entry, void *dummy) { struct lwp_info *lwp = (struct lwp_info *) entry; + struct thread_info *thread; struct thread_info *saved_inferior; CORE_ADDR pc; @@ -2427,7 +2425,9 @@ need_step_over_p (struct inferior_list_entry *entry, void *dummy) return 0; } - if (lwp->last_resume_kind == resume_stop) + thread = get_lwp_thread (lwp); + + if (thread->last_resume_kind == resume_stop) { if (debug_threads) fprintf (stderr, @@ -2474,7 +2474,7 @@ need_step_over_p (struct inferior_list_entry *entry, void *dummy) } saved_inferior = current_inferior; - current_inferior = get_lwp_thread (lwp); + current_inferior = thread; /* We can only step over breakpoints we know about. */ if (breakpoint_here (pc)) @@ -2802,6 +2802,7 @@ static void proceed_one_lwp (struct inferior_list_entry *entry) { struct lwp_info *lwp; + struct thread_info *thread; int step; lwp = (struct lwp_info *) entry; @@ -2817,7 +2818,9 @@ proceed_one_lwp (struct inferior_list_entry *entry) return; } - if (lwp->last_resume_kind == resume_stop) + thread = get_lwp_thread (lwp); + + if (thread->last_resume_kind == resume_stop) { if (debug_threads) fprintf (stderr, " client wants LWP %ld stopped\n", lwpid_of (lwp)); @@ -2839,7 +2842,7 @@ proceed_one_lwp (struct inferior_list_entry *entry) return; } - step = lwp->last_resume_kind == resume_step; + step = thread->last_resume_kind == resume_step; linux_resume_one_lwp (lwp, step, 0, NULL); } @@ -4032,6 +4035,10 @@ linux_async (int enable) { int previous = (linux_event_pipe[0] != -1); + if (debug_threads) + fprintf (stderr, "linux_async (%d), previous=%d\n", + enable, previous); + if (previous != enable) { sigset_t mask; @@ -4261,11 +4268,26 @@ linux_write_pc (struct regcache *regcache, CORE_ADDR pc) (*the_low_target.set_pc) (regcache, pc); } +static int +linux_thread_stopped (struct thread_info *thread) +{ + return get_thread_lwp (thread)->stopped; +} + +/* This exposes stop-all-threads functionality to other modules. */ + +static void +linux_pause_all (void) +{ + stop_all_lwps (); +} + static struct target_ops linux_target_ops = { linux_create_inferior, linux_attach, linux_kill, linux_detach, + linux_mourn, linux_join, linux_thread_alive, linux_resume, @@ -4308,7 +4330,9 @@ static struct target_ops linux_target_ops = { linux_process_qsupported, linux_supports_tracepoints, linux_read_pc, - linux_write_pc + linux_write_pc, + linux_thread_stopped, + linux_pause_all }; static void diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h index 4564f56d07..ec8f3f6aab 100644 --- a/gdb/gdbserver/linux-low.h +++ b/gdb/gdbserver/linux-low.h @@ -201,9 +201,6 @@ struct lwp_info and then processed and cleared in linux_resume_one_lwp. */ struct thread_resume *resume; - /* The last resume GDB requested on this thread. */ - enum resume_kind last_resume_kind; - /* True if the LWP was seen stop at an internal breakpoint and needs stepping over later when it is resumed. */ int need_step_over; @@ -229,7 +226,8 @@ struct lwp_info *find_lwp_pid (ptid_t ptid); /* From thread-db.c */ int thread_db_init (int use_events); -void thread_db_free (struct process_info *, int detaching); +void thread_db_detach (struct process_info *); +void thread_db_mourn (struct process_info *); int thread_db_handle_monitor_command (char *); int thread_db_get_tls_address (struct thread_info *thread, CORE_ADDR offset, CORE_ADDR load_module, CORE_ADDR *address); diff --git a/gdb/gdbserver/nto-low.c b/gdb/gdbserver/nto-low.c index 89933b91d9..854208291e 100644 --- a/gdb/gdbserver/nto-low.c +++ b/gdb/gdbserver/nto-low.c @@ -900,6 +900,7 @@ static struct target_ops nto_target_ops = { nto_attach, nto_kill, nto_detach, + NULL, /* nto_mourn */ NULL, /* nto_join */ nto_thread_alive, nto_resume, diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c index 7cd1e2d7cf..6a93c3ae20 100644 --- a/gdb/gdbserver/remote-utils.c +++ b/gdb/gdbserver/remote-utils.c @@ -92,6 +92,7 @@ int remote_debug = 0; struct ui_file *gdb_stdlog; static int remote_desc = INVALID_DESCRIPTOR; +static int listen_desc = INVALID_DESCRIPTOR; /* FIXME headerize? */ extern int using_threads; @@ -107,15 +108,88 @@ int transport_is_reliable = 0; # define write(fd, buf, len) send (fd, (char *) buf, len, 0) #endif +int +gdb_connected (void) +{ + return remote_desc != INVALID_DESCRIPTOR; +} + +static void +enable_async_notification (int fd) +{ +#if defined(F_SETFL) && defined (FASYNC) + int save_fcntl_flags; + + save_fcntl_flags = fcntl (fd, F_GETFL, 0); + fcntl (fd, F_SETFL, save_fcntl_flags | FASYNC); +#if defined (F_SETOWN) + fcntl (fd, F_SETOWN, getpid ()); +#endif +#endif +} + +static int +handle_accept_event (int err, gdb_client_data client_data) +{ + struct sockaddr_in sockaddr; + socklen_t tmp; + + if (debug_threads) + fprintf (stderr, "handling possible accept event\n"); + + tmp = sizeof (sockaddr); + remote_desc = accept (listen_desc, (struct sockaddr *) &sockaddr, &tmp); + if (remote_desc == -1) + perror_with_name ("Accept failed"); + + /* Enable TCP keep alive process. */ + tmp = 1; + setsockopt (remote_desc, SOL_SOCKET, SO_KEEPALIVE, + (char *) &tmp, sizeof (tmp)); + + /* Tell TCP not to delay small packets. This greatly speeds up + interactive response. */ + tmp = 1; + setsockopt (remote_desc, IPPROTO_TCP, TCP_NODELAY, + (char *) &tmp, sizeof (tmp)); + +#ifndef USE_WIN32API + close (listen_desc); /* No longer need this */ + + signal (SIGPIPE, SIG_IGN); /* If we don't do this, then gdbserver simply + exits when the remote side dies. */ +#else + closesocket (listen_desc); /* No longer need this */ +#endif + + delete_file_handler (listen_desc); + + /* Convert IP address to string. */ + fprintf (stderr, "Remote debugging from host %s\n", + inet_ntoa (sockaddr.sin_addr)); + + enable_async_notification (remote_desc); + + /* Register the event loop handler. */ + add_file_handler (remote_desc, handle_serial_event, NULL); + + /* We have a new GDB connection now. If we were disconnected + tracing, there's a window where the target could report a stop + event to the event loop, and since we have a connection now, we'd + try to send vStopped notifications to GDB. But, don't do that + until GDB as selected all-stop/non-stop, and has queried the + threads' status ('?'). */ + target_async (0); + + return 0; +} + /* Open a connection to a remote debugger. NAME is the filename used for communication. */ void remote_open (char *name) { -#if defined(F_SETFL) && defined (FASYNC) - int save_fcntl_flags; -#endif char *port_str; port_str = strchr (name, ':'); @@ -183,9 +257,14 @@ remote_open (char *name) #endif fprintf (stderr, "Remote debugging using %s\n", name); -#endif /* USE_WIN32API */ transport_is_reliable = 0; + + enable_async_notification (remote_desc); + + /* Register the event loop handler. */ + add_file_handler (remote_desc, handle_serial_event, NULL); +#endif /* USE_WIN32API */ } else { @@ -195,7 +274,6 @@ remote_open (char *name) int port; struct sockaddr_in sockaddr; socklen_t tmp; - int tmp_desc; char *port_end; port = strtoul (port_str + 1, &port_end, 10); @@ -212,21 +290,21 @@ remote_open (char *name) } #endif - tmp_desc = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP); - if (tmp_desc < 0) + listen_desc = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (listen_desc < 0) perror_with_name ("Can't open socket"); /* Allow rapid reuse of this port. */ tmp = 1; - setsockopt (tmp_desc, SOL_SOCKET, SO_REUSEADDR, (char *) &tmp, + setsockopt (listen_desc, SOL_SOCKET, SO_REUSEADDR, (char *) &tmp, sizeof (tmp)); sockaddr.sin_family = PF_INET; sockaddr.sin_port = htons (port); sockaddr.sin_addr.s_addr = INADDR_ANY; - if (bind (tmp_desc, (struct sockaddr *) &sockaddr, sizeof (sockaddr)) - || listen (tmp_desc, 1)) + if (bind (listen_desc, (struct sockaddr *) &sockaddr, sizeof (sockaddr)) + || listen (listen_desc, 1)) perror_with_name ("Can't bind address"); /* If port is zero, a random port will be selected, and the @@ -234,7 +312,7 @@ remote_open (char *name) if (port == 0) { socklen_t len = sizeof (sockaddr); - if (getsockname (tmp_desc, (struct sockaddr *) &sockaddr, &len) < 0 + if (getsockname (listen_desc, (struct sockaddr *) &sockaddr, &len) < 0 || len < sizeof (sockaddr)) perror_with_name ("Can't determine port"); port = ntohs (sockaddr.sin_port); @@ -243,49 +321,11 @@ remote_open (char *name) fprintf (stderr, "Listening on port %d\n", port); fflush (stderr); - tmp = sizeof (sockaddr); - remote_desc = accept (tmp_desc, (struct sockaddr *) &sockaddr, &tmp); - if (remote_desc == -1) - perror_with_name ("Accept failed"); - - /* Enable TCP keep alive process. */ - tmp = 1; - setsockopt (remote_desc, SOL_SOCKET, SO_KEEPALIVE, - (char *) &tmp, sizeof (tmp)); - - /* Tell TCP not to delay small packets. This greatly speeds up - interactive response. */ - tmp = 1; - setsockopt (remote_desc, IPPROTO_TCP, TCP_NODELAY, - (char *) &tmp, sizeof (tmp)); - - -#ifndef USE_WIN32API - close (tmp_desc); /* No longer need this */ - - signal (SIGPIPE, SIG_IGN); /* If we don't do this, then gdbserver simply - exits when the remote side dies. */ -#else - closesocket (tmp_desc); /* No longer need this */ -#endif - - /* Convert IP address to string. */ - fprintf (stderr, "Remote debugging from host %s\n", - inet_ntoa (sockaddr.sin_addr)); + /* Register the event loop handler. */ + add_file_handler (listen_desc, handle_accept_event, NULL); transport_is_reliable = 1; } - -#if defined(F_SETFL) && defined (FASYNC) - save_fcntl_flags = fcntl (remote_desc, F_GETFL, 0); - fcntl (remote_desc, F_SETFL, save_fcntl_flags | FASYNC); -#if defined (F_SETOWN) - fcntl (remote_desc, F_SETOWN, getpid ()); -#endif -#endif - - /* Register the event loop handler. */ - add_file_handler (remote_desc, handle_serial_event, NULL); } void @@ -298,6 +338,7 @@ remote_close (void) #else close (remote_desc); #endif + remote_desc = INVALID_DESCRIPTOR; } /* Convert hex digit A to a number. */ diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c index 0fd82eebea..8666bcd878 100644 --- a/gdb/gdbserver/server.c +++ b/gdb/gdbserver/server.c @@ -1392,6 +1392,7 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) strcat (own_buf, ";ConditionalTracepoints+"); strcat (own_buf, ";TraceStateVariables+"); strcat (own_buf, ";TracepointSource+"); + strcat (own_buf, ";DisconnectedTracing+"); } return; @@ -1976,31 +1977,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 @@ -2008,9 +2059,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 @@ -2019,9 +2069,18 @@ handle_status (char *own_buf) } else { + pause_all (); + 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"); } @@ -2390,7 +2449,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); @@ -2405,7 +2465,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), @@ -2418,9 +2478,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 (); + } + } } } @@ -2429,7 +2517,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; @@ -2455,9 +2543,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; @@ -2483,7 +2571,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 @@ -2727,7 +2857,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); @@ -2738,13 +2868,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; @@ -2785,7 +2913,7 @@ process_serial_event (void) last_status.kind = TARGET_WAITKIND_EXITED; last_status.value.sig = TARGET_SIGNAL_KILL; } - return; + return 0; } else { @@ -2826,27 +2954,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) @@ -2857,11 +2993,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) + { + mourn_inferior (process); + remove_process (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 = 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; } diff --git a/gdb/gdbserver/server.h b/gdb/gdbserver/server.h index dcff359972..e90e088b64 100644 --- a/gdb/gdbserver/server.h +++ b/gdb/gdbserver/server.h @@ -179,6 +179,9 @@ struct thread_info void *target_data; void *regcache_data; + /* The last resume GDB requested on this thread. */ + enum resume_kind last_resume_kind; + /* The last wait status reported for this thread. */ struct target_waitstatus last_status; @@ -224,8 +227,14 @@ struct process_info { struct inferior_list_entry head; + /* Nonzero if this child process was attached rather than + spawned. */ int attached; + /* True if GDB asked us to detach from this process, but we remained + attached anyway. */ + int gdb_detached; + /* The symbol cache. */ struct sym_cache *symbol_cache; @@ -327,7 +336,7 @@ extern int non_stop; /* Functions from event-loop.c. */ typedef void *gdb_client_data; -typedef void (handler_func) (int, gdb_client_data); +typedef int (handler_func) (int, gdb_client_data); extern void delete_file_handler (int fd); extern void add_file_handler (int fd, handler_func *proc, @@ -336,8 +345,8 @@ extern void add_file_handler (int fd, handler_func *proc, extern void start_event_loop (void); /* Functions from server.c. */ -extern void handle_serial_event (int err, gdb_client_data client_data); -extern void handle_target_event (int err, gdb_client_data client_data); +extern int handle_serial_event (int err, gdb_client_data client_data); +extern int handle_target_event (int err, gdb_client_data client_data); extern void push_event (ptid_t ptid, struct target_waitstatus *status); @@ -354,6 +363,8 @@ extern int all_symbols_looked_up; extern int noack_mode; extern int transport_is_reliable; +int gdb_connected (void); + ptid_t read_ptid (char *buf, char **obuf); char *write_ptid (char *buf, ptid_t ptid); @@ -499,6 +510,11 @@ char *phex_nz (ULONGEST l, int sizeof_l); void initialize_tracepoint (void); +extern int tracing; +extern int disconnected_tracing; + +void stop_tracing (void); + int handle_tracepoint_general_set (char *own_buf); int handle_tracepoint_query (char *own_buf); diff --git a/gdb/gdbserver/spu-low.c b/gdb/gdbserver/spu-low.c index b2eb913063..d171abc3fc 100644 --- a/gdb/gdbserver/spu-low.c +++ b/gdb/gdbserver/spu-low.c @@ -448,7 +448,6 @@ spu_wait (ptid_t ptid, struct target_waitstatus *ourstatus, int options) ourstatus->kind = TARGET_WAITKIND_EXITED; ourstatus->value.integer = WEXITSTATUS (w); clear_inferiors (); - remove_process (find_process_pid (ret)); return pid_to_ptid (ret); } else if (!WIFSTOPPED (w)) @@ -457,7 +456,6 @@ spu_wait (ptid_t ptid, struct target_waitstatus *ourstatus, int options) ourstatus->kind = TARGET_WAITKIND_SIGNALLED; ourstatus->value.sig = target_signal_from_host (WTERMSIG (w)); clear_inferiors (); - remove_process (find_process_pid (ret)); return pid_to_ptid (ret); } @@ -608,6 +606,7 @@ static struct target_ops spu_target_ops = { spu_attach, spu_kill, spu_detach, + NULL, /* mourn */ spu_join, spu_thread_alive, spu_resume, diff --git a/gdb/gdbserver/target.c b/gdb/gdbserver/target.c index f50bc02c3d..4fc8c07597 100644 --- a/gdb/gdbserver/target.c +++ b/gdb/gdbserver/target.c @@ -154,3 +154,45 @@ target_pid_to_str (ptid_t ptid) return buf; } + +/* Return a pretty printed form of target_waitstatus. */ + +const char * +target_waitstatus_to_string (const struct target_waitstatus *ws) +{ + static char buf[200]; + const char *kind_str = "status->kind = "; + + switch (ws->kind) + { + case TARGET_WAITKIND_EXITED: + sprintf (buf, "%sexited, status = %d", + kind_str, ws->value.integer); + break; + case TARGET_WAITKIND_STOPPED: + sprintf (buf, "%sstopped, signal = %s", + kind_str, target_signal_to_name (ws->value.sig)); + break; + case TARGET_WAITKIND_SIGNALLED: + sprintf (buf, "%ssignalled, signal = %s", + kind_str, target_signal_to_name (ws->value.sig)); + break; + case TARGET_WAITKIND_LOADED: + sprintf (buf, "%sloaded", kind_str); + break; + case TARGET_WAITKIND_EXECD: + sprintf (buf, "%sexecd", kind_str); + break; + case TARGET_WAITKIND_SPURIOUS: + sprintf (buf, "%sspurious", kind_str); + break; + case TARGET_WAITKIND_IGNORE: + sprintf (buf, "%signore", kind_str); + break; + default: + sprintf (buf, "%sunknown???", kind_str); + break; + } + + return buf; +} diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h index 6ee500982e..4cccb29394 100644 --- a/gdb/gdbserver/target.h +++ b/gdb/gdbserver/target.h @@ -138,6 +138,10 @@ struct target_ops int (*detach) (int pid); + /* The inferior process has died. Do what is right. */ + + void (*mourn) (struct process_info *proc); + /* Wait for inferior PID to exit. */ void (*join) (int pid); @@ -299,6 +303,12 @@ struct target_ops /* Write PC to REGCACHE. */ void (*write_pc) (struct regcache *regcache, CORE_ADDR pc); + + /* Return true if THREAD is known to be stopped now. */ + int (*thread_stopped) (struct thread_info *thread); + + /* Pause all threads. */ + void (*pause_all) (void); }; extern struct target_ops *the_target; @@ -317,6 +327,9 @@ void set_target_ops (struct target_ops *); #define detach_inferior(pid) \ (*the_target->detach) (pid) +#define mourn_inferior(PROC) \ + (*the_target->mourn) (PROC) + #define mythread_alive(pid) \ (*the_target->thread_alive) (pid) @@ -339,14 +352,27 @@ void set_target_ops (struct target_ops *); (the_target->supports_multi_process ? \ (*the_target->supports_multi_process) () : 0) -#define target_process_qsupported(query) \ - if (the_target->process_qsupported) \ - the_target->process_qsupported (query) +#define target_process_qsupported(query) \ + do \ + { \ + if (the_target->process_qsupported) \ + the_target->process_qsupported (query); \ + } while (0) #define target_supports_tracepoints() \ (the_target->supports_tracepoints \ ? (*the_target->supports_tracepoints) () : 0) +#define thread_stopped(thread) \ + (*the_target->thread_stopped) (thread) + +#define pause_all() \ + do \ + { \ + if (the_target->pause_all) \ + (*the_target->pause_all) (); \ + } while (0) + /* Start non-stop mode, returns 0 on success, -1 on failure. */ int start_non_stop (int nonstop); @@ -363,4 +389,6 @@ void set_desired_inferior (int id); const char *target_pid_to_str (ptid_t); +const char *target_waitstatus_to_string (const struct target_waitstatus *); + #endif /* TARGET_H */ diff --git a/gdb/gdbserver/thread-db.c b/gdb/gdbserver/thread-db.c index 55f953dfec..8e7d7a9361 100644 --- a/gdb/gdbserver/thread-db.c +++ b/gdb/gdbserver/thread-db.c @@ -743,7 +743,7 @@ thread_db_init (int use_events) if (use_events && thread_db_enable_reporting () == 0) { /* Keep trying; maybe event reporting will work later. */ - thread_db_free (proc, 0); + thread_db_mourn (proc); return 0; } thread_db_find_new_threads (); @@ -768,41 +768,64 @@ any_thread_of (struct inferior_list_entry *entry, void *args) /* Disconnect from libthread_db and free resources. */ -void -thread_db_free (struct process_info *proc, int detaching) +static void +disable_thread_event_reporting (struct process_info *proc) { struct thread_db *thread_db = proc->private->thread_db; if (thread_db) { - struct thread_info *saved_inferior; - int pid; - td_err_e (*td_ta_delete_p) (td_thragent_t *); td_err_e (*td_ta_clear_event_p) (const td_thragent_t *ta, td_thr_events_t *event); #ifndef USE_LIBTHREAD_DB_DIRECTLY td_ta_clear_event_p = dlsym (thread_db->handle, "td_ta_clear_event"); - td_ta_delete_p = dlsym (thread_db->handle, "td_ta_delete"); #else - td_ta_delete_p = &td_ta_delete; td_ta_clear_event_p = &td_ta_clear_event; #endif - pid = pid_of (proc); - saved_inferior = current_inferior; - current_inferior = - (struct thread_info *) find_inferior (&all_threads, - any_thread_of, &pid); - - if (detaching && td_ta_clear_event_p != NULL) + if (td_ta_clear_event_p != NULL) { + struct thread_info *saved_inferior; td_thr_events_t events; + int pid; + + pid = pid_of (proc); + saved_inferior = current_inferior; + current_inferior = + (struct thread_info *) find_inferior (&all_threads, + any_thread_of, &pid); /* Set the process wide mask saying we aren't interested in any events anymore. */ td_event_fillset (&events); (*td_ta_clear_event_p) (thread_db->thread_agent, &events); + + current_inferior = saved_inferior; } + } +} + +void +thread_db_detach (struct process_info *proc) +{ + disable_thread_event_reporting (proc); +} + +/* Disconnect from libthread_db and free resources. */ + +void +thread_db_mourn (struct process_info *proc) +{ + struct thread_db *thread_db = proc->private->thread_db; + if (thread_db) + { + td_err_e (*td_ta_delete_p) (td_thragent_t *); + +#ifndef USE_LIBTHREAD_DB_DIRECTLY + td_ta_delete_p = dlsym (thread_db->handle, "td_ta_delete"); +#else + td_ta_delete_p = &td_ta_delete; +#endif if (td_ta_delete_p != NULL) (*td_ta_delete_p) (thread_db->thread_agent); @@ -813,7 +836,6 @@ thread_db_free (struct process_info *proc, int detaching) free (thread_db); proc->private->thread_db = NULL; - current_inferior = saved_inferior; } } diff --git a/gdb/gdbserver/tracepoint.c b/gdb/gdbserver/tracepoint.c index df378ff4bb..c7970e32ca 100644 --- a/gdb/gdbserver/tracepoint.c +++ b/gdb/gdbserver/tracepoint.c @@ -558,7 +558,11 @@ static struct readonly_region *readonly_regions; /* The global that controls tracing overall. */ -static int tracing; +int tracing; + +/* Controls whether tracing should continue after GDB disconnects. */ + +int disconnected_tracing; /* The reason for the last tracing run to have stopped. We initialize to a distinct string so that GDB can distinguish between "stopped @@ -1699,7 +1703,7 @@ cmd_qtstart (char *packet) /* End a tracing run, filling in a stop reason to report back to GDB, and removing the tracepoints from the code. */ -static void +void stop_tracing (void) { if (!tracing) @@ -1736,6 +1740,11 @@ stop_tracing (void) tracing_stop_reason = eval_result_names[expr_eval_result]; tracing_stop_tpnum = error_tracepoint->number; } + else if (!gdb_connected ()) + { + trace_debug ("Stopping the trace because GDB disconnected"); + tracing_stop_reason = "tdisconnected"; + } else { trace_debug ("Stopping the trace because of a tstop command"); @@ -1756,6 +1765,21 @@ cmd_qtstop (char *packet) write_ok (packet); } +static void +cmd_qtdisconnected (char *own_buf) +{ + ULONGEST setting; + char *packet = own_buf; + + packet += strlen ("QTDisconnected:"); + + unpack_varlen_hex (packet, &setting); + + write_ok (own_buf); + + disconnected_tracing = setting; +} + static void cmd_qtframe (char *own_buf) { @@ -1853,13 +1877,19 @@ cmd_qtstatus (char *packet) convert_int_to_ascii ((gdb_byte *) result_name, p, strlen (result_name)); } - sprintf (packet, "T%d;%s:%x;tframes:%x;tcreated:%x;tfree:%x;tsize:%s;circular:%d", + sprintf (packet, + "T%d;" + "%s:%x;" + "tframes:%x;tcreated:%x;" + "tfree:%x;tsize:%s;" + "circular:%d;" + "disconn:%d", tracing ? 1 : 0, stop_reason_rsp, tracing_stop_tpnum, traceframe_count, traceframes_created, - free_space (), - phex_nz (trace_buffer_hi - trace_buffer_lo, 0), - circular_trace_buffer); + free_space (), phex_nz (trace_buffer_hi - trace_buffer_lo, 0), + circular_trace_buffer, + disconnected_tracing); } /* State variables to help return all the tracepoint bits. */ @@ -2168,6 +2198,12 @@ handle_tracepoint_general_set (char *packet) cmd_qtstop (packet); return 1; } + else if (strncmp ("QTDisconnected:", packet, + strlen ("QTDisconnected:")) == 0) + { + cmd_qtdisconnected (packet); + return 1; + } else if (strncmp ("QTFrame:", packet, strlen ("QTFrame:")) == 0) { cmd_qtframe (packet); diff --git a/gdb/gdbserver/win32-low.c b/gdb/gdbserver/win32-low.c index 904d6149ee..ea5de4cd98 100644 --- a/gdb/gdbserver/win32-low.c +++ b/gdb/gdbserver/win32-low.c @@ -1581,9 +1581,6 @@ win32_wait (ptid_t ptid, struct target_waitstatus *ourstatus, int options) case TARGET_WAITKIND_EXITED: OUTMSG2 (("Child exited with retcode = %x\n", ourstatus->value.integer)); - - process = find_process_pid (current_process_id); - remove_process (process); win32_clear_inferiors (); return pid_to_ptid (current_event.dwProcessId); case TARGET_WAITKIND_STOPPED: @@ -1755,6 +1752,7 @@ static struct target_ops win32_target_ops = { win32_attach, win32_kill, win32_detach, + NULL, win32_join, win32_thread_alive, win32_resume, -- 2.34.1