#include "server.h"
#include "linux-low.h"
extern int debug_threads;
#ifdef HAVE_THREAD_DB_H
#include <thread_db.h>
#endif
#define GDB_GREGSET_T elf_gregset_t
#define GDB_FPREGSET_T elf_fpregset_t
#ifndef HAVE_ELF_FPREGSET_T
#ifdef HAVE_LINUX_ELF_H
#include <linux/elf.h>
#endif
#endif
#include "../gdb_proc_service.h"
static struct ps_prochandle proc_handle;
static td_thragent_t *thread_agent;
static int find_new_threads_callback (const td_thrhandle_t *th_p, void *data);
static 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";
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 void
thread_db_create_event (CORE_ADDR where)
{
td_event_msg_t msg;
td_err_e err;
struct inferior_linux_data *tdata;
if (debug_threads)
fprintf (stderr, "Thread creation event.\n");
tdata = inferior_target_data (current_inferior);
err = td_ta_event_getmsg (thread_agent, &msg);
if (err != TD_OK)
fprintf (stderr, "thread getmsg err: %s\n",
thread_db_err_str (err));
find_new_threads_callback (msg.th_p, NULL);
}
#if 0
static void
thread_db_death_event (CORE_ADDR where)
{
if (debug_threads)
fprintf (stderr, "Thread death event.\n");
}
#endif
static int
thread_db_enable_reporting ()
{
td_thr_events_t events;
td_notify_t notify;
td_err_e err;
td_event_emptyset (&events);
td_event_addset (&events, TD_CREATE);
#if 0
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;
}
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
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 void
maybe_attach_thread (const td_thrhandle_t *th_p, td_thrinfo_t *ti_p)
{
td_err_e err;
struct thread_info *inferior;
struct process_info *process;
if (all_threads.head == all_threads.tail)
{
inferior = (struct thread_info *) all_threads.head;
process = get_thread_process (inferior);
if (process->thread_known == 0)
{
change_inferior_id (&all_threads, ti_p->ti_tid);
goto found;
}
}
inferior = (struct thread_info *) find_inferior_id (&all_threads,
ti_p->ti_tid);
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, ti_p->ti_tid);
inferior = (struct thread_info *) find_inferior_id (&all_threads,
ti_p->ti_tid);
if (inferior == NULL)
{
warning ("Could not attach to thread %ld (LWP %d)\n",
ti_p->ti_tid, ti_p->ti_lid);
return;
}
process = inferior_target_data (inferior);
found:
new_thread_notify (ti_p->ti_tid);
process->tid = ti_p->ti_tid;
process->lwpid = ti_p->ti_lid;
process->thread_known = 1;
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));
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;
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));
}
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_init ()
{
int err;
proc_handle.pid = ((struct inferior_list_entry *)current_inferior)->id;
err = td_ta_new (&proc_handle, &thread_agent);
switch (err)
{
case TD_NOLIBTHREAD:
return 0;
case TD_OK:
if (thread_db_enable_reporting () == 0)
return 0;
thread_db_find_new_threads ();
thread_db_look_up_symbols ();
return 1;
default:
warning ("error initializing thread_db library.");
}
return 0;
}