#include "defs.h"
#include "gdbthread.h"
#include "target.h"
#include "inferior.h"
#include "gdbcmd.h"
#include "regcache.h"
#include "gdb_wait.h"
#include <time.h>
#if defined(USE_PROC_FS) || defined(HAVE_GREGSET_T)
#include <sys/procfs.h>
#endif
#include "gdb_proc_service.h"
#if defined HAVE_STDINT_H
#if defined (HAVE_THREAD_DB_H)
#include <thread_db.h>
#else
#include "gdb_thread_db.h"
#endif
#include <dlfcn.h>
#include "gregset.h"
#define GET_PID(ptid) ptid_get_pid (ptid)
#define GET_LWP(ptid) ptid_get_lwp (ptid)
#define GET_THREAD(ptid) ptid_get_tid (ptid)
#define is_lwp(ptid) (GET_LWP (ptid) != 0)
#define is_thread(ptid) (GET_THREAD (ptid) != 0)
#define BUILD_LWP(lwp, pid) ptid_build (pid, lwp, 0)
#define BUILD_THREAD(tid, pid) ptid_build (pid, 0, tid)
extern int linux_child_wait (int, int *, int *);
extern void check_all_signal_numbers (void);
extern void linuxthreads_discard_global_state (void);
extern void attach_thread (int);
static struct target_ops *target_beneath;
static struct target_ops thread_db_ops;
#if defined(PROC_SERVICE_IS_OLD)
typedef const struct ps_prochandle *gdb_ps_prochandle_t;
typedef char *gdb_ps_read_buf_t;
typedef char *gdb_ps_write_buf_t;
typedef int gdb_ps_size_t;
#else
typedef struct ps_prochandle *gdb_ps_prochandle_t;
typedef void *gdb_ps_read_buf_t;
typedef const void *gdb_ps_write_buf_t;
typedef size_t gdb_ps_size_t;
#endif
ps_err_e
ps_pstop (gdb_ps_prochandle_t ph)
{
return PS_OK;
}
ps_err_e
ps_pcontinue (gdb_ps_prochandle_t ph)
{
return PS_OK;
}
ps_err_e
ps_lstop (gdb_ps_prochandle_t ph,
lwpid_t lwpid)
{
return PS_OK;
}
ps_err_e
ps_lcontinue (gdb_ps_prochandle_t ph,
lwpid_t lwpid)
{
return PS_OK;
}
ps_err_e
ps_lgetxregsize (gdb_ps_prochandle_t ph,
lwpid_t lwpid,
int *xregsize)
{
return PS_OK;
}
ps_err_e
ps_lgetxregs (gdb_ps_prochandle_t ph,
lwpid_t lwpid,
caddr_t xregset)
{
return PS_OK;
}
ps_err_e
ps_lsetxregs (gdb_ps_prochandle_t ph,
lwpid_t lwpid,
caddr_t xregset)
{
return PS_OK;
}
void
ps_plog (const char *fmt, ...)
{
va_list args;
va_start (args, fmt);
vfprintf_filtered (gdb_stderr, fmt, args);
}
ps_err_e
ps_pglobal_lookup (gdb_ps_prochandle_t ph,
const char *ld_object_name,
const char *ld_symbol_name,
paddr_t *ld_symbol_addr)
{
struct minimal_symbol *ms;
ms = lookup_minimal_symbol (ld_symbol_name, NULL, NULL);
if (!ms)
return PS_NOSYM;
*ld_symbol_addr = SYMBOL_VALUE_ADDRESS (ms);
return PS_OK;
}
static ps_err_e rw_common (const struct ps_prochandle *ph,
paddr_t addr,
char *buf,
int size,
int write_p);
enum {PS_READ = 0, PS_WRITE = 1};
ps_err_e
ps_pdread (gdb_ps_prochandle_t ph,
paddr_t addr,
gdb_ps_read_buf_t buf,
gdb_ps_size_t size)
{
return rw_common (ph, addr, buf, size, PS_READ);
}
ps_err_e
ps_pdwrite (gdb_ps_prochandle_t ph,
paddr_t addr,
gdb_ps_write_buf_t buf,
gdb_ps_size_t size)
{
return rw_common (ph, addr, (char *) buf, size, PS_WRITE);
}
ps_err_e
ps_ptread (gdb_ps_prochandle_t ph,
paddr_t addr,
gdb_ps_read_buf_t buf,
gdb_ps_size_t size)
{
return rw_common (ph, addr, buf, size, PS_READ);
}
ps_err_e
ps_ptwrite (gdb_ps_prochandle_t ph,
paddr_t addr,
gdb_ps_write_buf_t buf,
gdb_ps_size_t size)
{
return rw_common (ph, addr, (char *) buf, size, PS_WRITE);
}
static char *thr_err_string (td_err_e);
static char *thr_state_string (td_thr_state_e);
struct ps_prochandle main_prochandle;
td_thragent_t * main_threadagent;
static ps_err_e
rw_common (const struct ps_prochandle *ph,
paddr_t addr,
char *buf,
int size,
int write_p)
{
struct cleanup *old_chain = save_inferior_ptid ();
int to_do = size;
int done = 0;
inferior_ptid = pid_to_ptid (main_prochandle.pid);
while (to_do > 0)
{
done = current_target.to_xfer_memory (addr, buf, size, write_p,
¤t_target);
if (done <= 0)
{
if (write_p == PS_READ)
print_sys_errmsg ("rw_common (): read", errno);
else
print_sys_errmsg ("rw_common (): write", errno);
return PS_ERR;
}
to_do -= done;
buf += done;
}
do_cleanups (old_chain);
return PS_OK;
}
ps_err_e
ps_lgetregs (gdb_ps_prochandle_t ph,
lwpid_t lwpid,
prgregset_t gregset)
{
struct cleanup *old_chain = save_inferior_ptid ();
inferior_ptid = BUILD_LWP (lwpid, main_prochandle.pid);
current_target.to_fetch_registers (-1);
fill_gregset ((gdb_gregset_t *) gregset, -1);
do_cleanups (old_chain);
return PS_OK;
}
ps_err_e
ps_lsetregs (gdb_ps_prochandle_t ph,
lwpid_t lwpid,
const prgregset_t gregset)
{
struct cleanup *old_chain = save_inferior_ptid ();
inferior_ptid = BUILD_LWP (lwpid, main_prochandle.pid);
supply_gregset ((gdb_gregset_t *) gregset);
current_target.to_store_registers (-1);
do_cleanups (old_chain);
return PS_OK;
}
ps_err_e
ps_lgetfpregs (gdb_ps_prochandle_t ph,
lwpid_t lwpid,
gdb_prfpregset_t *fpregset)
{
struct cleanup *old_chain = save_inferior_ptid ();
inferior_ptid = BUILD_LWP (lwpid, main_prochandle.pid);
current_target.to_fetch_registers (-1);
fill_fpregset (fpregset, -1);
do_cleanups (old_chain);
return PS_OK;
}
ps_err_e
ps_lsetfpregs (gdb_ps_prochandle_t ph,
lwpid_t lwpid,
const gdb_prfpregset_t *fpregset)
{
struct cleanup *old_chain = save_inferior_ptid ();
inferior_ptid = BUILD_LWP (lwpid, main_prochandle.pid);
supply_fpregset (fpregset);
current_target.to_store_registers (-1);
do_cleanups (old_chain);
return PS_OK;
}
pid_t
ps_getpid (gdb_ps_prochandle_t ph)
{
return ph->pid;
}
#ifdef TM_I386SOL2_H
ps_err_e
ps_lgetLDT (gdb_ps_prochandle_t ph, lwpid_t lwpid,
struct ssd *pldt)
{
extern struct ssd *procfs_find_LDT_entry (int);
struct ssd *ret;
ret = procfs_find_LDT_entry (BUILD_LWP (lwpid,
PIDGET (main_prochandle.pid)));
if (ret)
{
memcpy (pldt, ret, sizeof (struct ssd));
return PS_OK;
}
else
return PS_ERR;
}
#endif
static td_err_e (*p_td_init) (void);
static td_err_e (*p_td_ta_new) (const struct ps_prochandle *ph_p,
td_thragent_t **ta_pp);
static td_err_e (*p_td_ta_delete) (td_thragent_t *ta_p);
static td_err_e (*p_td_ta_get_nthreads) (const td_thragent_t *ta_p,
int *nthread_p);
static td_err_e (*p_td_ta_thr_iter) (const td_thragent_t *ta_p,
td_thr_iter_f *cb,
void *cbdata_p,
td_thr_state_e state,
int ti_pri,
sigset_t *ti_sigmask_p,
unsigned ti_user_flags);
static td_err_e (*p_td_ta_event_addr) (const td_thragent_t *ta_p,
u_long event,
td_notify_t *notify_p);
static td_err_e (*p_td_ta_event_getmsg) (const td_thragent_t *ta_p,
td_event_msg_t *msg);
static td_err_e (*p_td_ta_set_event) (const td_thragent_t *ta_p,
td_thr_events_t *events);
static td_err_e (*p_td_thr_validate) (const td_thrhandle_t *th_p);
static td_err_e (*p_td_thr_event_enable) (const td_thrhandle_t *th_p,
int on_off);
static td_err_e (*p_td_thr_get_info) (const td_thrhandle_t *th_p,
td_thrinfo_t *ti_p);
static td_err_e (*p_td_thr_getgregs) (const td_thrhandle_t *th_p,
prgregset_t regset);
static td_err_e (*p_td_thr_setgregs) (const td_thrhandle_t *th_p,
const prgregset_t regset);
static td_err_e (*p_td_thr_getfpregs) (const td_thrhandle_t *th_p,
gdb_prfpregset_t *fpregset);
static td_err_e (*p_td_thr_setfpregs) (const td_thrhandle_t *th_p,
const gdb_prfpregset_t *fpregset);
static td_err_e (*p_td_ta_map_id2thr) (const td_thragent_t *ta_p,
thread_t tid,
td_thrhandle_t *th_p);
static td_err_e (*p_td_ta_map_lwp2thr) (const td_thragent_t *ta_p,
lwpid_t lwpid,
td_thrhandle_t *th_p);
static int
init_thread_db_library (void)
{
void *dlhandle;
td_err_e ret;
if ((dlhandle = dlopen ("libthread_db.so.1", RTLD_NOW)) == NULL)
return 0;
if ((p_td_init = dlsym (dlhandle, "td_init")) == NULL)
return 0;
if ((p_td_ta_new = dlsym (dlhandle, "td_ta_new")) == NULL)
return 0;
if ((p_td_ta_delete = dlsym (dlhandle, "td_ta_delete")) == NULL)
return 0;
if ((p_td_ta_map_id2thr = dlsym (dlhandle, "td_ta_map_id2thr")) == NULL)
return 0;
if ((p_td_ta_map_lwp2thr = dlsym (dlhandle, "td_ta_map_lwp2thr")) == NULL)
return 0;
if ((p_td_ta_get_nthreads = dlsym (dlhandle, "td_ta_get_nthreads")) == NULL)
return 0;
if ((p_td_ta_thr_iter = dlsym (dlhandle, "td_ta_thr_iter")) == NULL)
return 0;
if ((p_td_thr_validate = dlsym (dlhandle, "td_thr_validate")) == NULL)
return 0;
if ((p_td_thr_get_info = dlsym (dlhandle, "td_thr_get_info")) == NULL)
return 0;
if ((p_td_thr_getgregs = dlsym (dlhandle, "td_thr_getgregs")) == NULL)
return 0;
if ((p_td_thr_setgregs = dlsym (dlhandle, "td_thr_setgregs")) == NULL)
return 0;
if ((p_td_thr_getfpregs = dlsym (dlhandle, "td_thr_getfpregs")) == NULL)
return 0;
if ((p_td_thr_setfpregs = dlsym (dlhandle, "td_thr_setfpregs")) == NULL)
return 0;
ret = p_td_init ();
if (ret != TD_OK)
{
warning ("init_thread_db: td_init: %s", thr_err_string (ret));
return 0;
}
p_td_ta_event_addr = dlsym (dlhandle, "td_ta_event_addr");
p_td_ta_event_getmsg = dlsym (dlhandle, "td_ta_event_getmsg");
p_td_ta_set_event = dlsym (dlhandle, "td_ta_set_event");
p_td_thr_event_enable = dlsym (dlhandle, "td_thr_event_enable");
return 1;
}
static char *
thr_err_string (td_err_e errcode)
{
static char buf[50];
switch (errcode) {
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";
default:
sprintf (buf, "unknown thread_db error '%d'", errcode);
return buf;
}
}
static char *
thr_state_string (td_thr_state_e statecode)
{
static char buf[50];
switch (statecode) {
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:
sprintf (buf, "unknown thread_db state %d", statecode);
return buf;
}
}
typedef struct THREADINFO {
thread_t tid;
pid_t lid;
td_thr_state_e state;
td_thr_type_e type;
int pending;
int status;
} threadinfo;
threadinfo * threadlist;
int threadlist_max = 0;
int threadlist_top = 0;
#define THREADLIST_ALLOC 100
static threadinfo *
insert_thread (int tid, int lid, td_thr_state_e state, td_thr_type_e type)
{
if (threadlist_top >= threadlist_max)
{
threadlist_max += THREADLIST_ALLOC;
threadlist = xrealloc (threadlist,
threadlist_max * sizeof (threadinfo));
if (threadlist == NULL)
return NULL;
}
threadlist[threadlist_top].tid = tid;
threadlist[threadlist_top].lid = lid;
threadlist[threadlist_top].state = state;
threadlist[threadlist_top].type = type;
threadlist[threadlist_top].pending = 0;
threadlist[threadlist_top].status = 0;
return &threadlist[threadlist_top++];
}
static void
empty_threadlist (void)
{
threadlist_top = 0;
}
static threadinfo *
next_pending_event (void)
{
int i;
for (i = 0; i < threadlist_top; i++)
if (threadlist[i].pending)
return &threadlist[i];
return NULL;
}
static void
threadlist_iter (int (*func) (), void *data, td_thr_state_e state,
td_thr_type_e type)
{
int i;
for (i = 0; i < threadlist_top; i++)
if ((state == TD_THR_ANY_STATE || state == threadlist[i].state) &&
(type == TD_THR_ANY_TYPE || type == threadlist[i].type))
if ((*func) (&threadlist[i], data) != 0)
break;
return;
}
extern int using_thread_db;
static int event_pid;
static int attach_pid;
static CORE_ADDR thread_creation_bkpt_address;
static CORE_ADDR thread_death_bkpt_address;
static void
enable_thread_event_reporting (td_thragent_t *ta)
{
td_thr_events_t events;
td_notify_t notify;
CORE_ADDR addr;
if (p_td_ta_set_event == NULL ||
p_td_ta_event_addr == NULL ||
p_td_ta_event_getmsg == NULL ||
p_td_thr_event_enable == NULL)
return;
td_event_emptyset (&events);
td_event_addset (&events, TD_CREATE);
td_event_addset (&events, TD_DEATH);
if (p_td_ta_set_event (ta, &events) != TD_OK)
{
warning ("unable to set global thread event mask");
return;
}
remove_thread_event_breakpoints ();
if (p_td_ta_event_addr (ta, TD_CREATE, ¬ify) != TD_OK)
{
warning ("unable to get location for thread creation breakpoint");
return;
}
create_thread_event_breakpoint ((CORE_ADDR) notify.u.bptaddr);
thread_creation_bkpt_address = (CORE_ADDR) notify.u.bptaddr;
if (p_td_ta_event_addr (ta, TD_DEATH, ¬ify) != TD_OK)
{
warning ("unable to get location for thread death breakpoint");
return;
}
create_thread_event_breakpoint ((CORE_ADDR) notify.u.bptaddr);
thread_death_bkpt_address = (CORE_ADDR) notify.u.bptaddr;
}
static void
disable_thread_event_reporting (td_thragent_t *ta)
{
td_thr_events_t events;
td_event_emptyset (&events);
p_td_ta_set_event (main_threadagent, &events);
remove_thread_event_breakpoints ();
thread_creation_bkpt_address = 0;
thread_death_bkpt_address = 0;
}
static int
check_for_thread_event (struct target_waitstatus *tws, int event_pid)
{
return 0;
}
static void
thread_db_push_target (void)
{
push_target (&thread_db_ops);
target_beneath = find_target_beneath (&thread_db_ops);
using_thread_db = 1;
enable_thread_event_reporting (main_threadagent);
}
static void
thread_db_unpush_target (void)
{
using_thread_db = 0;
target_beneath = NULL;
empty_threadlist ();
p_td_ta_delete (main_threadagent);
unpush_target (&thread_db_ops);
linuxthreads_discard_global_state ();
}
static void (*target_new_objfile_chain) (struct objfile *objfile);
static int stop_or_attach_thread_callback (const td_thrhandle_t *th,
void *data);
static int wait_thread_callback (const td_thrhandle_t *th,
void *data);
static void
thread_db_new_objfile (struct objfile *objfile)
{
td_err_e ret;
if (using_thread_db)
goto quit;
if (objfile == NULL)
goto quit;
main_prochandle.pid = PIDGET (inferior_ptid);
ret = p_td_ta_new (&main_prochandle, &main_threadagent);
switch (ret) {
default:
warning ("Unexpected error initializing thread_db: %s",
thr_err_string (ret));
break;
case TD_NOLIBTHREAD:
break;
case TD_OK:
thread_db_push_target ();
event_pid = PIDGET (inferior_ptid);
p_td_ta_thr_iter (main_threadagent,
stop_or_attach_thread_callback,
(void *) 0,
TD_THR_ANY_STATE,
TD_THR_LOWEST_PRIORITY,
TD_SIGNO_MASK,
TD_THR_ANY_USER_FLAGS);
p_td_ta_thr_iter (main_threadagent,
wait_thread_callback,
(void *) 0,
TD_THR_ANY_STATE,
TD_THR_LOWEST_PRIORITY,
TD_SIGNO_MASK,
TD_THR_ANY_USER_FLAGS);
break;
}
quit:
if (target_new_objfile_chain)
target_new_objfile_chain (objfile);
}
static int
thread_db_alive (ptid_t ptid)
{
if (is_thread (ptid))
{
td_thrhandle_t th;
td_err_e ret;
int pid = GET_THREAD (ptid);
if ((ret = p_td_ta_map_id2thr (main_threadagent, pid, &th)) != TD_OK)
return 0;
if ((ret = p_td_thr_validate (&th)) != TD_OK)
return 0;
return 1;
}
else if (target_beneath->to_thread_alive)
return target_beneath->to_thread_alive (ptid);
else
return 0;
}
static int
get_lwp_from_thread_handle (td_thrhandle_t *th)
{
td_thrinfo_t ti;
td_err_e ret;
if ((ret = p_td_thr_get_info (th, &ti)) != TD_OK)
error ("get_lwp_from_thread_handle: thr_get_info failed: %s",
thr_err_string (ret));
return ti.ti_lid;
}
static int
get_lwp_from_thread_id (int tid )
{
td_thrhandle_t th;
td_err_e ret;
if ((ret = p_td_ta_map_id2thr (main_threadagent, tid, &th)) != TD_OK)
error ("get_lwp_from_thread_id: map_id2thr failed: %s",
thr_err_string (ret));
return get_lwp_from_thread_handle (&th);
}
static char *
thread_db_pid_to_str (ptid_t ptid)
{
static char buf[100];
td_thrhandle_t th;
td_thrinfo_t ti;
td_err_e ret;
if (is_thread (ptid))
{
if ((ret = p_td_ta_map_id2thr (main_threadagent,
GET_THREAD (ptid),
&th)) != TD_OK)
error ("thread_db: map_id2thr failed: %s", thr_err_string (ret));
if ((ret = p_td_thr_get_info (&th, &ti)) != TD_OK)
error ("thread_db: thr_get_info failed: %s", thr_err_string (ret));
if (ti.ti_state == TD_THR_ACTIVE &&
ti.ti_lid != 0)
sprintf (buf, "Thread %ld (LWP %d)", ti.ti_tid, ti.ti_lid);
else
sprintf (buf, "Thread %ld (%s)", ti.ti_tid,
thr_state_string (ti.ti_state));
}
else if (GET_LWP (ptid))
sprintf (buf, "LWP %ld", GET_LWP (ptid));
else return normal_pid_to_str (ptid);
return buf;
}
static void
thread_db_files_info (struct target_ops *tgt_vector)
{
printf_filtered ("thread_db stratum:\n");
target_beneath->to_files_info (tgt_vector);
}
static int
thread_db_xfer_memory (CORE_ADDR memaddr, char *myaddr, int len, int dowrite,
struct mem_attrib *attrib,
struct target_ops *target)
{
struct cleanup *old_chain;
int ret;
old_chain = save_inferior_ptid ();
if (is_thread (inferior_ptid) ||
!target_thread_alive (inferior_ptid))
{
inferior_ptid = pid_to_ptid (main_prochandle.pid);
}
ret = target_beneath->to_xfer_memory (memaddr, myaddr, len,
dowrite, attrib, target);
do_cleanups (old_chain);
return ret;
}
static void
thread_db_fetch_registers (int regno)
{
td_thrhandle_t thandle;
gdb_prfpregset_t fpregset;
prgregset_t gregset;
thread_t thread;
td_err_e ret;
if (!is_thread (inferior_ptid))
{
target_beneath->to_fetch_registers (regno);
return;
}
if ((thread = GET_THREAD (inferior_ptid)) == 0)
error ("fetch_registers: thread == 0");
if ((ret = p_td_ta_map_id2thr (main_threadagent, thread, &thandle)) != TD_OK)
error ("fetch_registers: td_ta_map_id2thr: %s", thr_err_string (ret));
if ((ret = p_td_thr_getgregs (&thandle, gregset)) != TD_OK &&
ret != TD_PARTIALREG)
error ("fetch_registers: td_thr_getgregs %s", thr_err_string (ret));
if ((ret = p_td_thr_getfpregs (&thandle, &fpregset)) != TD_OK &&
ret != TD_NOFPREGS)
error ("fetch_registers: td_thr_getfpregs %s", thr_err_string (ret));
supply_gregset ((gdb_gregset_t *) gregset);
supply_fpregset (&fpregset);
}
static void
thread_db_store_registers (int regno)
{
td_thrhandle_t thandle;
gdb_prfpregset_t fpregset;
prgregset_t gregset;
thread_t thread;
td_err_e ret;
if (!is_thread (inferior_ptid))
{
target_beneath->to_store_registers (regno);
return;
}
if ((thread = GET_THREAD (inferior_ptid)) == 0)
error ("store_registers: thread == 0");
if ((ret = p_td_ta_map_id2thr (main_threadagent, thread, &thandle)) != TD_OK)
error ("store_registers: td_ta_map_id2thr %s", thr_err_string (ret));
if (regno != -1)
{
char old_value[REGISTER_SIZE];
memcpy (old_value, ®isters[REGISTER_BYTE (regno)], REGISTER_SIZE);
if ((ret = p_td_thr_getgregs (&thandle, gregset)) != TD_OK)
error ("store_registers: td_thr_getgregs %s", thr_err_string (ret));
if ((ret = p_td_thr_getfpregs (&thandle, &fpregset)) != TD_OK)
error ("store_registers: td_thr_getfpregs %s", thr_err_string (ret));
memcpy (®isters[REGISTER_BYTE (regno)], old_value, REGISTER_SIZE);
}
fill_gregset ((gdb_gregset_t *) gregset, regno);
fill_fpregset (&fpregset, regno);
if ((ret = p_td_thr_setgregs (&thandle, gregset)) != TD_OK)
error ("store_registers: td_thr_setgregs %s", thr_err_string (ret));
if ((ret = p_td_thr_setfpregs (&thandle, &fpregset)) != TD_OK &&
ret != TD_NOFPREGS)
error ("store_registers: td_thr_setfpregs %s", thr_err_string (ret));
}
static void
handle_new_thread (int tid,
int lid,
int verbose)
{
ptid_t gdb_ptid = BUILD_THREAD (tid, main_prochandle.pid);
int wait_pid, wait_status;
if (verbose)
printf_filtered ("[New %s]\n", target_pid_to_str (gdb_ptid));
add_thread (gdb_ptid);
if (lid != main_prochandle.pid)
{
attach_thread (lid);
attach_pid = lid;
}
}
static void
test_for_new_thread (int tid, int lid, int verbose)
{
if (!in_thread_list (BUILD_THREAD (tid, main_prochandle.pid)))
handle_new_thread (tid, lid, verbose);
}
static int
find_new_threads_callback (const td_thrhandle_t *th, void *ignored)
{
td_thrinfo_t ti;
td_err_e ret;
if ((ret = p_td_thr_get_info (th, &ti)) != TD_OK)
{
warning ("find_new_threads_callback: %s", thr_err_string (ret));
return -1;
}
test_for_new_thread (ti.ti_tid, ti.ti_lid, 0);
return 0;
}
static void
thread_db_find_new_threads (void)
{
if (PIDGET (inferior_ptid) == -1)
{
printf_filtered ("No process.\n");
return;
}
p_td_ta_thr_iter (main_threadagent,
find_new_threads_callback,
(void *) 0,
TD_THR_ANY_STATE,
TD_THR_LOWEST_PRIORITY,
TD_SIGNO_MASK,
TD_THR_ANY_USER_FLAGS);
if (target_beneath->to_find_new_threads)
target_beneath->to_find_new_threads ();
}
static int
resume_thread_callback (const td_thrhandle_t *th, void *data)
{
td_thrinfo_t ti;
td_err_e ret;
if ((ret = p_td_thr_get_info (th, &ti)) != TD_OK)
{
warning ("resume_thread_callback: %s", thr_err_string (ret));
return -1;
}
test_for_new_thread (ti.ti_tid, ti.ti_lid, 1);
if (ti.ti_lid != main_prochandle.pid &&
ti.ti_lid != event_pid)
{
target_beneath->to_resume (pid_to_ptid (ti.ti_lid), 0, 0);
}
return 0;
}
static int
new_resume_thread_callback (threadinfo *thread, void *data)
{
if (thread->lid != event_pid &&
thread->lid != main_prochandle.pid)
{
target_beneath->to_resume (pid_to_ptid (thread->lid), 0, 0);
}
return 0;
}
static int last_resume_pid;
static int last_resume_step;
static int last_resume_signo;
static void
thread_db_resume (ptid_t ptid, int step, enum target_signal signo)
{
last_resume_pid = PIDGET (ptid);
last_resume_step = step;
last_resume_signo = signo;
if (PIDGET (ptid) != -1)
{
if (is_thread (ptid))
ptid = pid_to_ptid (get_lwp_from_thread_id (GET_THREAD (ptid)));
else if (GET_LWP (ptid))
ptid = pid_to_ptid (GET_LWP (ptid));
}
if (PIDGET (ptid) != -1 && step)
{
target_beneath->to_resume (ptid, step, signo);
}
else
{
threadlist_iter (new_resume_thread_callback,
(void *) 0,
TD_THR_ANY_STATE,
TD_THR_ANY_TYPE);
if (event_pid)
{
target_beneath->to_resume (pid_to_ptid (event_pid), step, signo);
}
if (event_pid != main_prochandle.pid)
{
target_beneath->to_resume (pid_to_ptid (main_prochandle.pid), 0, 0);
}
}
}
static int
stop_or_attach_thread_callback (const td_thrhandle_t *th, void *data)
{
td_thrinfo_t ti;
td_err_e ret;
ptid_t gdb_ptid;
int on_off = 1;
if ((ret = p_td_thr_get_info (th, &ti)) != TD_OK)
{
warning ("stop_or_attach_thread_callback: %s", thr_err_string (ret));
return -1;
}
insert_thread (ti.ti_tid, ti.ti_lid, ti.ti_state, ti.ti_type);
gdb_ptid = BUILD_THREAD (ti.ti_tid, main_prochandle.pid);
if (!in_thread_list (gdb_ptid))
{
handle_new_thread (ti.ti_tid, ti.ti_lid, 1);
if (p_td_thr_event_enable)
if ((ret = p_td_thr_event_enable (th, on_off)) != TD_OK)
warning ("stop_or_attach_thread: %s", thr_err_string (ret));
}
else if (ti.ti_lid != event_pid &&
ti.ti_lid != main_prochandle.pid)
{
ret = (td_err_e) kill (ti.ti_lid, SIGSTOP);
}
return 0;
}
static void
wait_for_stop (int pid)
{
int i;
int retpid;
int status;
#if defined (NSIG)
static int wstatus [NSIG];
#elif defined (_NSIG)
static int wstatus [_NSIG];
#else
#error No definition for number of signals!
#endif
memset (&wstatus, 0, sizeof (wstatus));
do {
errno = 0;
if (pid == main_prochandle.pid)
retpid = waitpid (pid, &status, 0);
else
retpid = waitpid (pid, &status, __WCLONE);
if (retpid > 0)
if (WSTOPSIG (status) == SIGSTOP)
{
for (i = 0; i < sizeof(wstatus) / sizeof (wstatus[0]); i++)
if (wstatus[i])
if (i != SIGSTOP)
{
kill (retpid, i);
}
break;
}
else
{
int signo;
if (WIFEXITED (status))
error ("Ack! Thread Exited event. What do I do now???");
else if (WIFSTOPPED (status))
signo = WSTOPSIG (status);
else
signo = WTERMSIG (status);
if (retpid != event_pid &&
signo == SIGTRAP &&
breakpoint_inserted_here_p (read_pc_pid (pid_to_ptid (retpid)) -
DECR_PC_AFTER_BREAK))
{
if (DECR_PC_AFTER_BREAK)
write_pc_pid (read_pc_pid (pid_to_ptid (retpid))
- DECR_PC_AFTER_BREAK,
pid_to_ptid (retpid));
}
else if (retpid != event_pid && signo == SIGINT)
{
;
}
else
{
wstatus [signo] = 1;
}
child_resume (pid_to_ptid (retpid), 0, TARGET_SIGNAL_0);
continue;
}
} while (errno == 0 || errno == EINTR);
}
static int
wait_thread_callback (const td_thrhandle_t *th, void *data)
{
td_thrinfo_t ti;
td_err_e ret;
if ((ret = p_td_thr_get_info (th, &ti)) != TD_OK)
{
warning ("wait_thread_callback: %s", thr_err_string (ret));
return -1;
}
if (ti.ti_lid == event_pid ||
ti.ti_lid == main_prochandle.pid)
return 0;
wait_for_stop (ti.ti_lid);
return 0;
}
static int
new_wait_thread_callback (threadinfo *thread, void *data)
{
if (thread->lid != event_pid &&
thread->lid != main_prochandle.pid)
{
wait_for_stop (thread->lid);
}
return 0;
}
static ptid_t
thread_db_wait (ptid_t ptid, struct target_waitstatus *ourstatus)
{
td_thrhandle_t thandle;
td_thrinfo_t ti;
td_err_e ret;
lwpid_t lwp;
int retpid;
ptid_t retptid;
int status;
int save_errno;
check_all_signal_numbers ();
event_pid = 0;
attach_pid = 0;
#if 0
if (PIDGET (ptid) == -1)
lwp = -1;
else
lwp = get_lwp_from_thread_id (GET_THREAD (ptid));
#endif
save_errno = linux_child_wait (-1, &retpid, &status);
store_waitstatus (ourstatus, status);
if (ourstatus->kind == TARGET_WAITKIND_EXITED)
return pid_to_ptid (retpid);
event_pid = retpid;
if (last_resume_step == 0 || last_resume_pid == -1)
{
if (retpid != main_prochandle.pid)
{
kill (main_prochandle.pid, SIGSTOP);
wait_for_stop (main_prochandle.pid);
}
empty_threadlist ();
p_td_ta_thr_iter (main_threadagent,
stop_or_attach_thread_callback,
(void *) 0,
TD_THR_ANY_STATE,
TD_THR_LOWEST_PRIORITY,
TD_SIGNO_MASK,
TD_THR_ANY_USER_FLAGS);
threadlist_iter (new_wait_thread_callback,
(void *) 0,
TD_THR_ANY_STATE,
TD_THR_ANY_TYPE);
}
#if 0
if ((lwp = GET_LWP (pid_to_ptid (retpid))) == 0)
#endif
lwp = retpid;
if ((ret = p_td_ta_map_lwp2thr (main_threadagent, lwp, &thandle)) != TD_OK)
return pid_to_ptid (retpid);
if ((ret = p_td_thr_validate (&thandle)) != TD_OK)
return pid_to_ptid (retpid);
if ((ret = p_td_thr_get_info (&thandle, &ti)) != TD_OK)
{
warning ("thread_db: thr_get_info failed ('%s')", thr_err_string (ret));
return pid_to_ptid (retpid);
}
retptid = BUILD_THREAD (ti.ti_tid, main_prochandle.pid);
if (!in_thread_list (retptid))
{
printf_filtered ("[New %s]\n", target_pid_to_str (retptid));
add_thread (retptid);
}
#if 0
check_for_thread_event (ourstatus, retpid);
#endif
return retptid;
}
static int
kill_thread_callback (const td_thrhandle_t *th, void *data)
{
td_thrinfo_t ti;
td_err_e ret;
if ((ret = p_td_thr_get_info (th, &ti)) != TD_OK)
{
warning ("kill_thread_callback: %s", thr_err_string (ret));
return -1;
}
if (ti.ti_lid != main_prochandle.pid)
{
kill (ti.ti_lid, SIGKILL);
}
return 0;
}
static void thread_db_kill (void)
{
int rpid;
int status;
if (! ptid_equal (inferior_ptid, null_ptid))
{
p_td_ta_thr_iter (main_threadagent,
kill_thread_callback,
(void *) 0,
TD_THR_ANY_STATE,
TD_THR_LOWEST_PRIORITY,
TD_SIGNO_MASK,
TD_THR_ANY_USER_FLAGS);
disable_thread_event_reporting (main_threadagent);
inferior_ptid = pid_to_ptid (main_prochandle.pid);
target_beneath->to_kill ();
}
do
{
rpid = waitpid (-1, &status, __WCLONE | WNOHANG);
}
while (rpid > 0 || errno == EINTR);
do
{
rpid = waitpid (-1, &status, WNOHANG);
}
while (rpid > 0 || errno == EINTR);
}
static void thread_db_mourn_inferior (void)
{
thread_db_unpush_target ();
target_mourn_inferior ();
}
static int
detach_thread_callback (const td_thrhandle_t *th, void *data)
{
td_thrinfo_t ti;
td_err_e ret;
if ((ret = p_td_thr_get_info (th, &ti)) != TD_OK)
{
warning ("detach_thread_callback: %s", thr_err_string (ret));
return -1;
}
if (!in_thread_list (BUILD_THREAD (ti.ti_tid, main_prochandle.pid)))
return 0;
if (ti.ti_lid != main_prochandle.pid)
{
struct cleanup *old_chain;
int off = 0;
if (p_td_thr_event_enable &&
(ret = p_td_thr_event_enable (th, off)) != TD_OK)
{
warning ("detach_thread_callback: %s\n", thr_err_string (ret));
return 0;
}
old_chain = save_inferior_ptid ();
inferior_ptid = pid_to_ptid (ti.ti_lid);
detach (TARGET_SIGNAL_0);
do_cleanups (old_chain);
}
return 0;
}
static void
thread_db_detach (char *args, int from_tty)
{
td_err_e ret;
if ((ret = p_td_ta_thr_iter (main_threadagent,
detach_thread_callback,
(void *) 0,
TD_THR_ANY_STATE,
TD_THR_LOWEST_PRIORITY,
TD_SIGNO_MASK,
TD_THR_ANY_USER_FLAGS))
!= TD_OK)
warning ("detach (thr_iter): %s", thr_err_string (ret));
disable_thread_event_reporting (main_threadagent);
thread_db_unpush_target ();
inferior_ptid = pid_to_ptid (PIDGET (inferior_ptid));
target_detach (args, from_tty);
}
static void
thread_db_create_inferior (char *exec_file, char *allargs, char **env)
{
thread_db_unpush_target ();
find_default_create_inferior (exec_file, allargs, env);
}
void
init_thread_db_ops (void)
{
thread_db_ops.to_shortname = "multi-thread";
thread_db_ops.to_longname = "multi-threaded child process.";
thread_db_ops.to_doc = "Threads and pthreads support.";
thread_db_ops.to_files_info = thread_db_files_info;
thread_db_ops.to_create_inferior = thread_db_create_inferior;
thread_db_ops.to_detach = thread_db_detach;
thread_db_ops.to_wait = thread_db_wait;
thread_db_ops.to_resume = thread_db_resume;
thread_db_ops.to_mourn_inferior = thread_db_mourn_inferior;
thread_db_ops.to_kill = thread_db_kill;
thread_db_ops.to_xfer_memory = thread_db_xfer_memory;
thread_db_ops.to_fetch_registers = thread_db_fetch_registers;
thread_db_ops.to_store_registers = thread_db_store_registers;
thread_db_ops.to_thread_alive = thread_db_alive;
thread_db_ops.to_find_new_threads = thread_db_find_new_threads;
thread_db_ops.to_pid_to_str = thread_db_pid_to_str;
thread_db_ops.to_stratum = thread_stratum;
thread_db_ops.to_has_thread_control = tc_schedlock;
thread_db_ops.to_magic = OPS_MAGIC;
}
#endif
void
_initialize_thread_db (void)
{
#ifdef HAVE_STDINT_H
if (init_thread_db_library ())
{
init_thread_db_ops ();
add_target (&thread_db_ops);
target_new_objfile_chain = target_new_objfile_hook;
target_new_objfile_hook = thread_db_new_objfile;
}
#endif
}