* 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) <last_resume_kind>: New field.
<attached>: Add comment.
<gdb_detached>: 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) <qSupported>: 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) <mourn, thread_stopped, pause_all>:
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 <pedro@codesourcery.com>
+
+ 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) <last_resume_kind>: New field.
+ <attached>: Add comment.
+ <gdb_detached>: 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) <qSupported>: 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) <mourn, thread_stopped, pause_all>:
+ 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 <pedro@codesourcery.com>
* regcache.c (realloc_register_cache): Invalidate inferior's
#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. */
free (event_ptr);
/* Now call the procedure associated with the event. */
- (*proc) (fd);
+ if ((*proc) (fd))
+ return -1;
return 1;
}
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;
/* 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
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
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);
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. */
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 ();
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
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;
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;
}
return -1;
#ifdef USE_THREAD_DB
- thread_db_free (process, 1);
+ thread_db_detach (process);
#endif
current_inferior =
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)
{
/* 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;
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)
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
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
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
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. */
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
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". */
{
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;
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));
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");
/* 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. */
&& (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",
}
lwp->resume = &r->resume[ndx];
- lwp->last_resume_kind = lwp->resume->kind;
+ thread->last_resume_kind = lwp->resume->kind;
return 0;
}
}
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;
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,
}
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))
proceed_one_lwp (struct inferior_list_entry *entry)
{
struct lwp_info *lwp;
+ struct thread_info *thread;
int step;
lwp = (struct lwp_info *) 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));
return;
}
- step = lwp->last_resume_kind == resume_step;
+ step = thread->last_resume_kind == resume_step;
linux_resume_one_lwp (lwp, step, 0, NULL);
}
{
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;
(*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,
linux_process_qsupported,
linux_supports_tracepoints,
linux_read_pc,
- linux_write_pc
+ linux_write_pc,
+ linux_thread_stopped,
+ linux_pause_all
};
static void
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;
/* 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);
nto_attach,
nto_kill,
nto_detach,
+ NULL, /* nto_mourn */
NULL, /* nto_join */
nto_thread_alive,
nto_resume,
struct ui_file *gdb_stdlog;
static int remote_desc = INVALID_DESCRIPTOR;
+static int listen_desc = INVALID_DESCRIPTOR;
/* FIXME headerize? */
extern int using_threads;
# 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, ':');
#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
{
int port;
struct sockaddr_in sockaddr;
socklen_t tmp;
- int tmp_desc;
char *port_end;
port = strtoul (port_str + 1, &port_end, 10);
}
#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
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);
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
#else
close (remote_desc);
#endif
+ remote_desc = INVALID_DESCRIPTOR;
}
/* Convert hex digit A to a number. */
strcat (own_buf, ";ConditionalTracepoints+");
strcat (own_buf, ";TraceStateVariables+");
strcat (own_buf, ";TracepointSource+");
+ strcat (own_buf, ";DisconnectedTracing+");
}
return;
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
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
}
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");
}
{
noack_mode = 0;
multi_process = 0;
- non_stop = 0;
+ /* Be sure we're out of tfind mode. */
+ current_traceframe = -1;
remote_open (port);
}
/* 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),
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 ();
+ }
+ }
}
}
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;
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;
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
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);
{
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;
last_status.kind = TARGET_WAITKIND_EXITED;
last_status.value.sig = TARGET_SIGNAL_KILL;
}
- return;
+ return 0;
}
else
{
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)
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;
}
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;
{
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;
/* 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,
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);
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);
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);
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))
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);
}
spu_attach,
spu_kill,
spu_detach,
+ NULL, /* mourn */
spu_join,
spu_thread_alive,
spu_resume,
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;
+}
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);
/* 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;
#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)
(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);
const char *target_pid_to_str (ptid_t);
+const char *target_waitstatus_to_string (const struct target_waitstatus *);
+
#endif /* TARGET_H */
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 ();
/* 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);
free (thread_db);
proc->private->thread_db = NULL;
- current_inferior = saved_inferior;
}
}
/* 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
/* 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)
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");
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)
{
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. */
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);
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:
win32_attach,
win32_kill,
win32_detach,
+ NULL,
win32_join,
win32_thread_alive,
win32_resume,