
* server.h (non_stop): Declare. (gdb_client_data, handler_func): Declare. (delete_file_handler, add_file_handler, start_event_loop): Declare. (handle_serial_event, handle_target_event, push_event) (putpkt_notif): Declare. * target.h (enum resume_kind): New. (struct thread_resume): Replace `step' field by `kind' field. (TARGET_WNOHANG): Define. (struct target_ops) <wait>: Add `options' argument. <supports_non_stop, async, start_non_stop>: New fields. (target_supports_non_stop, target_async): New. (start_non_stop): Declare. (mywait): Add `options' argument. * target.c (mywait): Add `options' argument. Print child exit notifications here. (start_non_stop): New. * server.c (non_stop, own_buf, mem_buf): New globals. (struct vstop_notif): New. (notif_queue): New global. (queue_stop_reply, push_event, discard_queued_stop_replies) (send_next_stop_reply): New. (start_inferior): Adjust to use resume_kind. Adjust to mywait interface changes. (attach_inferior): In non-stop mode, don't wait for the target here. (handle_general_set): Handle QNonStop. (handle_query): When handling qC, return the current general thread, instead of the first thread of the list. (handle_query): If the backend supports non-stop mode, include QNonStop+ in the qSupported query response. (handle_v_cont): Adjust to use resume_kind. Handle resume_stop and non-stop mode. (handle_v_attach, handle_v_run): Handle non-stop mode. (handle_v_stopped): New. (handle_v_requests): Report support for vCont;t. Handle vStopped. (myresume): Adjust to use resume_kind. Handle non-stop. (queue_stop_reply_callback): New. (handle_status): Handle non-stop mode. (main): Clear non_stop flag on reconnection. Use the event-loop. Refactor serial protocol handling from here ... (process_serial_event): ... to this new function. When GDB selects any thread, select one here. In non-stop mode, wait until GDB acks all pending events before exiting. (handle_serial_event, handle_target_event): New. * remote-utils.c (remote_open): Install remote_desc in the event loop. (remote_close): Remove remote_desc from the event loop. (putpkt_binary): Rename to... (putpkt_binary_1): ... this. Add `is_notic' argument. Handle it. (putpkt_binary): New as wrapper around putpkt_binary_1. (putpkt_notif): New. (prepare_resume_reply): In non-stop mode, don't change the general_thread. * event-loop.c: New. * Makefile.in (OBJ): Add event-loop.o. (event-loop.o): New rule. * linux-low.h (pid_of): Moved here. (lwpid_of): New. (get_lwp_thread): Use lwpid_of. (struct lwp_info): Delete `lwpid' field. Add `suspended' field. * linux-low.c (pid_of): Delete. (inferior_pid): Use lwpid_of. (linux_event_pipe): New. (target_is_async_p): New. (delete_lwp): New. (handle_extended_wait): Use lwpid_of. (add_lwp): Don't set lwpid field. (linux_attach_lwp): Adjust debug output. Use lwpid_of. (linux_kill_one_lwp): If killing a running lwp, stop it first. Use lwpid_of. Adjust to linux_wait_for_event interface changes. (linux_detach_one_lwp): If detaching from a running lwp, stop it first. Adjust to linux_wait_for_event interface changes. Use lwpid_of. (linux_detach): Don't delete the main lwp here. (linux_join): Use my_waitpid. Avoid signal_pid. Use lwpid_of. (status_pending_p): Don't consider explicitly suspended lwps. (linux_wait_for_lwp): Take an integer pid instead of a lwp_info pointer. Add OPTIONS argument. Change return type to int. Use my_waitpid instead of sleeping. Handle WNOHANG. Use lwpid_of. (linux_wait_for_event): Take an integer pid instead of a lwp_info pointer. Add status pointer argument. Return a pid instead of a status. Use lwpid_of. Adjust to linux_wait_for_lwp interface changes. In non-stop mode, don't switch to a random thread. (linux_wait): Rename to... (linux_wait_1): ... this. Add target_options argument, and handle it. Adjust to use resume_kind. Use lwpid_of. In non-stop mode, don't handle the continue thread. Handle TARGET_WNOHANG. Merge clean exit and signal exit code. Don't stop all threads in non-stop mode. In all-stop mode, only stop all threads when reporting a stop to GDB. Handle explicit thread stop requests. (async_file_flush, async_file_mark): New. (linux_wait): New. (send_sigstop): Use lwpid_of. (wait_for_sigstop): Use lwpid_of. Adjust to linux_wait_for_event interface changes. In non-stop mode, don't switch to a random thread. (linux_resume_one_lwp): Use lwpid_of. (linux_continue_one_thread, linux_queue_one_thread): Merge into ... (linux_resume_one_thread): ... this. Handle resume_stop. In non-stop mode, don't look for pending flag in all threads. (resume_status_pending_p): Don't consider explicitly suspended threads. (my_waitpid): Reimplement. Emulate __WALL. (linux_request_interrupt, linux_read_offsets, linux_xfer_siginfo): Use lwpid_of. (sigchld_handler, linux_supports_non_stop, linux_async) (linux_start_non_stop): New. (linux_target_ops): Register linux_supports_non_stop, linux_async and linux_start_non_stop. (initialize_low): Install SIGCHLD handler. * thread-db.c (thread_db_create_event, find_one_thread) (thread_db_get_tls_address): Use lwpid_of. * win32-low.c (win32_detach): Adjust to use resume_kind. (win32_wait): Add `options' argument. * spu-low.c (spu_resume): Adjust to use resume_kind. (spu_wait): Add `options' argument.
457 lines
12 KiB
C
457 lines
12 KiB
C
/* Thread management interface, for the remote server for GDB.
|
|
Copyright (C) 2002, 2004, 2005, 2006, 2007, 2008, 2009
|
|
Free Software Foundation, Inc.
|
|
|
|
Contributed by MontaVista Software.
|
|
|
|
This file is part of GDB.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
|
|
|
#include "server.h"
|
|
|
|
#include "linux-low.h"
|
|
|
|
extern int debug_threads;
|
|
|
|
static int thread_db_use_events;
|
|
|
|
#ifdef HAVE_THREAD_DB_H
|
|
#include <thread_db.h>
|
|
#endif
|
|
|
|
#include "gdb_proc_service.h"
|
|
|
|
#include <stdint.h>
|
|
|
|
/* Structure that identifies the child process for the
|
|
<proc_service.h> interface. */
|
|
static struct ps_prochandle proc_handle;
|
|
|
|
/* Connection to the libthread_db library. */
|
|
static td_thragent_t *thread_agent;
|
|
|
|
static int find_one_thread (int);
|
|
static int find_new_threads_callback (const td_thrhandle_t *th_p, void *data);
|
|
|
|
static const char *
|
|
thread_db_err_str (td_err_e err)
|
|
{
|
|
static char buf[64];
|
|
|
|
switch (err)
|
|
{
|
|
case TD_OK:
|
|
return "generic 'call succeeded'";
|
|
case TD_ERR:
|
|
return "generic error";
|
|
case TD_NOTHR:
|
|
return "no thread to satisfy query";
|
|
case TD_NOSV:
|
|
return "no sync handle to satisfy query";
|
|
case TD_NOLWP:
|
|
return "no LWP to satisfy query";
|
|
case TD_BADPH:
|
|
return "invalid process handle";
|
|
case TD_BADTH:
|
|
return "invalid thread handle";
|
|
case TD_BADSH:
|
|
return "invalid synchronization handle";
|
|
case TD_BADTA:
|
|
return "invalid thread agent";
|
|
case TD_BADKEY:
|
|
return "invalid key";
|
|
case TD_NOMSG:
|
|
return "no event message for getmsg";
|
|
case TD_NOFPREGS:
|
|
return "FPU register set not available";
|
|
case TD_NOLIBTHREAD:
|
|
return "application not linked with libthread";
|
|
case TD_NOEVENT:
|
|
return "requested event is not supported";
|
|
case TD_NOCAPAB:
|
|
return "capability not available";
|
|
case TD_DBERR:
|
|
return "debugger service failed";
|
|
case TD_NOAPLIC:
|
|
return "operation not applicable to";
|
|
case TD_NOTSD:
|
|
return "no thread-specific data for this thread";
|
|
case TD_MALLOC:
|
|
return "malloc failed";
|
|
case TD_PARTIALREG:
|
|
return "only part of register set was written/read";
|
|
case TD_NOXREGS:
|
|
return "X register set not available for this thread";
|
|
#ifdef HAVE_TD_VERSION
|
|
case TD_VERSION:
|
|
return "version mismatch between libthread_db and libpthread";
|
|
#endif
|
|
default:
|
|
snprintf (buf, sizeof (buf), "unknown thread_db error '%d'", err);
|
|
return buf;
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
static char *
|
|
thread_db_state_str (td_thr_state_e state)
|
|
{
|
|
static char buf[64];
|
|
|
|
switch (state)
|
|
{
|
|
case TD_THR_STOPPED:
|
|
return "stopped by debugger";
|
|
case TD_THR_RUN:
|
|
return "runnable";
|
|
case TD_THR_ACTIVE:
|
|
return "active";
|
|
case TD_THR_ZOMBIE:
|
|
return "zombie";
|
|
case TD_THR_SLEEP:
|
|
return "sleeping";
|
|
case TD_THR_STOPPED_ASLEEP:
|
|
return "stopped by debugger AND blocked";
|
|
default:
|
|
snprintf (buf, sizeof (buf), "unknown thread_db state %d", state);
|
|
return buf;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static int
|
|
thread_db_create_event (CORE_ADDR where)
|
|
{
|
|
td_event_msg_t msg;
|
|
td_err_e err;
|
|
struct lwp_info *lwp;
|
|
|
|
if (debug_threads)
|
|
fprintf (stderr, "Thread creation event.\n");
|
|
|
|
/* FIXME: This assumes we don't get another event.
|
|
In the LinuxThreads implementation, this is safe,
|
|
because all events come from the manager thread
|
|
(except for its own creation, of course). */
|
|
err = td_ta_event_getmsg (thread_agent, &msg);
|
|
if (err != TD_OK)
|
|
fprintf (stderr, "thread getmsg err: %s\n",
|
|
thread_db_err_str (err));
|
|
|
|
/* If we do not know about the main thread yet, this would be a good time to
|
|
find it. We need to do this to pick up the main thread before any newly
|
|
created threads. */
|
|
lwp = get_thread_lwp (current_inferior);
|
|
if (lwp->thread_known == 0)
|
|
find_one_thread (lwpid_of (lwp));
|
|
|
|
/* msg.event == TD_EVENT_CREATE */
|
|
|
|
find_new_threads_callback (msg.th_p, NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if 0
|
|
static int
|
|
thread_db_death_event (CORE_ADDR where)
|
|
{
|
|
if (debug_threads)
|
|
fprintf (stderr, "Thread death event.\n");
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static int
|
|
thread_db_enable_reporting ()
|
|
{
|
|
td_thr_events_t events;
|
|
td_notify_t notify;
|
|
td_err_e err;
|
|
|
|
/* Set the process wide mask saying which events we're interested in. */
|
|
td_event_emptyset (&events);
|
|
td_event_addset (&events, TD_CREATE);
|
|
|
|
#if 0
|
|
/* This is reported to be broken in glibc 2.1.3. A different approach
|
|
will be necessary to support that. */
|
|
td_event_addset (&events, TD_DEATH);
|
|
#endif
|
|
|
|
err = td_ta_set_event (thread_agent, &events);
|
|
if (err != TD_OK)
|
|
{
|
|
warning ("Unable to set global thread event mask: %s",
|
|
thread_db_err_str (err));
|
|
return 0;
|
|
}
|
|
|
|
/* Get address for thread creation breakpoint. */
|
|
err = td_ta_event_addr (thread_agent, TD_CREATE, ¬ify);
|
|
if (err != TD_OK)
|
|
{
|
|
warning ("Unable to get location for thread creation breakpoint: %s",
|
|
thread_db_err_str (err));
|
|
return 0;
|
|
}
|
|
set_breakpoint_at ((CORE_ADDR) (unsigned long) notify.u.bptaddr,
|
|
thread_db_create_event);
|
|
|
|
#if 0
|
|
/* Don't concern ourselves with reported thread deaths, only
|
|
with actual thread deaths (via wait). */
|
|
|
|
/* Get address for thread death breakpoint. */
|
|
err = td_ta_event_addr (thread_agent, TD_DEATH, ¬ify);
|
|
if (err != TD_OK)
|
|
{
|
|
warning ("Unable to get location for thread death breakpoint: %s",
|
|
thread_db_err_str (err));
|
|
return;
|
|
}
|
|
set_breakpoint_at ((CORE_ADDR) (unsigned long) notify.u.bptaddr,
|
|
thread_db_death_event);
|
|
#endif
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
find_one_thread (int lwpid)
|
|
{
|
|
td_thrhandle_t th;
|
|
td_thrinfo_t ti;
|
|
td_err_e err;
|
|
struct thread_info *inferior;
|
|
struct lwp_info *lwp;
|
|
|
|
inferior = (struct thread_info *) find_inferior_id (&all_threads, lwpid);
|
|
lwp = get_thread_lwp (inferior);
|
|
if (lwp->thread_known)
|
|
return 1;
|
|
|
|
/* Get information about this thread. */
|
|
err = td_ta_map_lwp2thr (thread_agent, lwpid_of (lwp), &th);
|
|
if (err != TD_OK)
|
|
error ("Cannot get thread handle for LWP %d: %s",
|
|
lwpid, thread_db_err_str (err));
|
|
|
|
err = td_thr_get_info (&th, &ti);
|
|
if (err != TD_OK)
|
|
error ("Cannot get thread info for LWP %d: %s",
|
|
lwpid, thread_db_err_str (err));
|
|
|
|
if (debug_threads)
|
|
fprintf (stderr, "Found thread %ld (LWP %d)\n",
|
|
ti.ti_tid, ti.ti_lid);
|
|
|
|
if (lwpid_of (lwp) != ti.ti_lid)
|
|
{
|
|
warning ("PID mismatch! Expected %ld, got %ld",
|
|
(long) lwpid_of (lwp), (long) ti.ti_lid);
|
|
return 0;
|
|
}
|
|
|
|
if (thread_db_use_events)
|
|
{
|
|
err = td_thr_event_enable (&th, 1);
|
|
if (err != TD_OK)
|
|
error ("Cannot enable thread event reporting for %d: %s",
|
|
ti.ti_lid, thread_db_err_str (err));
|
|
}
|
|
|
|
/* If the new thread ID is zero, a final thread ID will be available
|
|
later. Do not enable thread debugging yet. */
|
|
if (ti.ti_tid == 0)
|
|
return 0;
|
|
|
|
lwp->thread_known = 1;
|
|
lwp->th = th;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void
|
|
maybe_attach_thread (const td_thrhandle_t *th_p, td_thrinfo_t *ti_p)
|
|
{
|
|
td_err_e err;
|
|
struct thread_info *inferior;
|
|
struct lwp_info *lwp;
|
|
|
|
inferior = (struct thread_info *) find_inferior_id (&all_threads,
|
|
ti_p->ti_lid);
|
|
if (inferior != NULL)
|
|
return;
|
|
|
|
if (debug_threads)
|
|
fprintf (stderr, "Attaching to thread %ld (LWP %d)\n",
|
|
ti_p->ti_tid, ti_p->ti_lid);
|
|
linux_attach_lwp (ti_p->ti_lid);
|
|
inferior = (struct thread_info *) find_inferior_id (&all_threads,
|
|
ti_p->ti_lid);
|
|
if (inferior == NULL)
|
|
{
|
|
warning ("Could not attach to thread %ld (LWP %d)\n",
|
|
ti_p->ti_tid, ti_p->ti_lid);
|
|
return;
|
|
}
|
|
|
|
lwp = inferior_target_data (inferior);
|
|
|
|
lwp->thread_known = 1;
|
|
lwp->th = *th_p;
|
|
|
|
if (thread_db_use_events)
|
|
{
|
|
err = td_thr_event_enable (th_p, 1);
|
|
if (err != TD_OK)
|
|
error ("Cannot enable thread event reporting for %d: %s",
|
|
ti_p->ti_lid, thread_db_err_str (err));
|
|
}
|
|
}
|
|
|
|
static int
|
|
find_new_threads_callback (const td_thrhandle_t *th_p, void *data)
|
|
{
|
|
td_thrinfo_t ti;
|
|
td_err_e err;
|
|
|
|
err = td_thr_get_info (th_p, &ti);
|
|
if (err != TD_OK)
|
|
error ("Cannot get thread info: %s", thread_db_err_str (err));
|
|
|
|
/* Check for zombies. */
|
|
if (ti.ti_state == TD_THR_UNKNOWN || ti.ti_state == TD_THR_ZOMBIE)
|
|
return 0;
|
|
|
|
maybe_attach_thread (th_p, &ti);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
thread_db_find_new_threads (void)
|
|
{
|
|
td_err_e err;
|
|
|
|
/* This function is only called when we first initialize thread_db.
|
|
First locate the initial thread. If it is not ready for
|
|
debugging yet, then stop. */
|
|
if (find_one_thread (all_threads.head->id) == 0)
|
|
return;
|
|
|
|
/* Iterate over all user-space threads to discover new threads. */
|
|
err = td_ta_thr_iter (thread_agent, find_new_threads_callback, NULL,
|
|
TD_THR_ANY_STATE, TD_THR_LOWEST_PRIORITY,
|
|
TD_SIGNO_MASK, TD_THR_ANY_USER_FLAGS);
|
|
if (err != TD_OK)
|
|
error ("Cannot find new threads: %s", thread_db_err_str (err));
|
|
}
|
|
|
|
/* Cache all future symbols that thread_db might request. We can not
|
|
request symbols at arbitrary states in the remote protocol, only
|
|
when the client tells us that new symbols are available. So when
|
|
we load the thread library, make sure to check the entire list. */
|
|
|
|
static void
|
|
thread_db_look_up_symbols (void)
|
|
{
|
|
const char **sym_list = td_symbol_list ();
|
|
CORE_ADDR unused;
|
|
|
|
for (sym_list = td_symbol_list (); *sym_list; sym_list++)
|
|
look_up_one_symbol (*sym_list, &unused);
|
|
}
|
|
|
|
int
|
|
thread_db_get_tls_address (struct thread_info *thread, CORE_ADDR offset,
|
|
CORE_ADDR load_module, CORE_ADDR *address)
|
|
{
|
|
#if HAVE_TD_THR_TLS_GET_ADDR
|
|
psaddr_t addr;
|
|
td_err_e err;
|
|
struct lwp_info *lwp;
|
|
|
|
lwp = get_thread_lwp (thread);
|
|
if (!lwp->thread_known)
|
|
find_one_thread (lwpid_of (lwp));
|
|
if (!lwp->thread_known)
|
|
return TD_NOTHR;
|
|
|
|
/* Note the cast through uintptr_t: this interface only works if
|
|
a target address fits in a psaddr_t, which is a host pointer.
|
|
So a 32-bit debugger can not access 64-bit TLS through this. */
|
|
err = td_thr_tls_get_addr (&lwp->th, (psaddr_t) (uintptr_t) load_module,
|
|
offset, &addr);
|
|
if (err == TD_OK)
|
|
{
|
|
*address = (CORE_ADDR) (uintptr_t) addr;
|
|
return 0;
|
|
}
|
|
else
|
|
return err;
|
|
#else
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
int
|
|
thread_db_init (int use_events)
|
|
{
|
|
int err;
|
|
|
|
/* FIXME drow/2004-10-16: This is the "overall process ID", which
|
|
GNU/Linux calls tgid, "thread group ID". When we support
|
|
attaching to threads, the original thread may not be the correct
|
|
thread. We would have to get the process ID from /proc for NPTL.
|
|
For LinuxThreads we could do something similar: follow the chain
|
|
of parent processes until we find the highest one we're attached
|
|
to, and use its tgid.
|
|
|
|
This isn't the only place in gdbserver that assumes that the first
|
|
process in the list is the thread group leader. */
|
|
proc_handle.pid = ((struct inferior_list_entry *)current_inferior)->id;
|
|
|
|
/* Allow new symbol lookups. */
|
|
all_symbols_looked_up = 0;
|
|
|
|
thread_db_use_events = use_events;
|
|
|
|
err = td_ta_new (&proc_handle, &thread_agent);
|
|
switch (err)
|
|
{
|
|
case TD_NOLIBTHREAD:
|
|
/* No thread library was detected. */
|
|
return 0;
|
|
|
|
case TD_OK:
|
|
/* The thread library was detected. */
|
|
|
|
if (use_events && thread_db_enable_reporting () == 0)
|
|
return 0;
|
|
thread_db_find_new_threads ();
|
|
thread_db_look_up_symbols ();
|
|
all_symbols_looked_up = 1;
|
|
return 1;
|
|
|
|
default:
|
|
warning ("error initializing thread_db library: %s",
|
|
thread_db_err_str (err));
|
|
}
|
|
|
|
return 0;
|
|
}
|