#include "defs.h"
#include "gdbthread.h"
#include "target.h"
#include "inferior.h"
#include "regcache.h"
#include <fcntl.h>
#define boolean_t int
#include <thread.h>
#undef boolean_t
#include <synch.h>
#include "gregset.h"
#ifndef SP_ARG0
#define SP_ARG0 (1 * 4)
#endif
#define DEBUG 0
#define DEBUG_FILE "/dev/tty"
#if !DEBUG
# define DBG(fmt_and_args)
# define DBG2(fmt_and_args)
#else
# define DBG(fmt_and_args) dbg fmt_and_args
# define DBG2(fmt_and_args)
#endif
#define CALL_BASE_1(call) \
do { \
DBG2(("CALL_BASE(" #call ")")); \
call; \
do_cleanups (infpid_cleanup); \
} while (0)
#define CALL_BASE(call) \
do { \
if (!lwp_infpid ()) \
error ("uw-thread: no lwp"); \
CALL_BASE_1 (call); \
} while (0)
#define TRY_BASE(call, called) \
do { \
if ((*(called) = lwp_infpid ())) \
CALL_BASE_1 (call); \
} while (0)
typedef struct {
struct thread_map map;
__lwp_desc_t lwp;
CORE_ADDR mapp;
} iter_t;
struct private_thread_info {
int stable;
int thrid;
int lwpid;
CORE_ADDR mapp;
};
extern struct target_ops procfs_ops;
extern int procfs_suppress_run;
static struct target_ops uw_thread_ops;
static struct target_ops base_ops;
static void (*target_new_objfile_chain)(struct objfile *);
static int uw_thread_active;
static CORE_ADDR thr_debug_addr;
static CORE_ADDR thr_brk_addr;
static CORE_ADDR thr_map_main;
static struct thread_info *switchto_thread;
static struct cleanup *infpid_cleanup;
#if DEBUG
static void
dbg (char *fmt, ...)
{
static int fd = -1, len;
va_list args;
char buf[1024];
char *path;
if (!fmt)
return;
if (fd < 0)
{
path = getenv ("UWTHR_DEBUG");
if (!path)
path = DEBUG_FILE;
if ((fd = open (path, O_WRONLY | O_CREAT | O_TRUNC, 0664)) < 0)
error ("can't open %s\n", path);
}
va_start (args, fmt);
vsprintf (buf, fmt, args);
va_end (args);
len = strlen (buf);
buf[len] = '\n';
(void)write (fd, buf, len + 1);
}
#if 0
static char *
dbgpid (ptid_t ptid)
{
static char *buf, buf1[80], buf2[80];
if (!buf || buf == buf2)
buf = buf1;
else
buf = buf2;
if (PIDGET (ptid) <= 0)
sprintf (buf, "%d", PIDGET (ptid));
else
sprintf (buf, "%s %ld/%d", ISTID (pid) ? "thr" : "lwp",
TIDGET (pid), PIDGET (pid));
return buf;
}
static char *
dbgchange (enum thread_change change)
{
switch (change) {
case tc_invalid: return "invalid";
case tc_thread_create: return "thread_create";
case tc_thread_exit: return "thread_exit";
case tc_switch_begin: return "switch_begin";
case tc_switch_complete: return "switch_complete";
case tc_cancel_complete: return "cancel_complete";
case tc_thread_suspend: return "thread_suspend";
case tc_thread_suspend_pending: return "thread_suspend_pending";
case tc_thread_continue: return "thread_continue";
default: return "unknown";
}
}
static char *
dbgstate (int state)
{
switch (state) {
case TS_ONPROC: return "running";
case TS_SLEEP: return "sleeping";
case TS_RUNNABLE: return "runnable";
case TS_ZOMBIE: return "zombie";
case TS_SUSPENDED: return "suspended";
#ifdef TS_FORK
case TS_FORK: return "forking";
#endif
default: return "confused";
}
}
#endif
#endif
static int
read_thr_debug (struct thread_debug *debugp)
{
return base_ops.to_xfer_memory (thr_debug_addr, (char *)debugp,
sizeof (*debugp), 0, NULL, &base_ops);
}
static int
read_map (CORE_ADDR mapp, struct thread_map *map)
{
return base_ops.to_xfer_memory ((CORE_ADDR)THR_MAP (mapp), (char *)map,
sizeof (*map), 0, NULL, &base_ops);
}
static int
read_lwp (CORE_ADDR lwpp, __lwp_desc_t *lwp)
{
return base_ops.to_xfer_memory (lwpp, (char *)lwp,
sizeof (*lwp), 0, NULL, &base_ops);
}
static int
thread_iter (int (*func)(iter_t *, void *), void *data)
{
struct thread_debug debug;
CORE_ADDR first, mapp;
iter_t iter;
int ret;
if (!read_thr_debug (&debug))
return 0;
if (!base_ops.to_xfer_memory ((CORE_ADDR)debug.thr_map, (char *)&mapp,
sizeof (mapp), 0, NULL, &base_ops))
return 0;
if (!mapp)
return 0;
for (first = mapp;;)
{
if (!read_map (mapp, &iter.map))
return 0;
if (iter.map.thr_lwpp)
if (!read_lwp ((CORE_ADDR)iter.map.thr_lwpp, &iter.lwp))
return 0;
iter.mapp = mapp;
if ((ret = func (&iter, data)))
return ret;
mapp = (CORE_ADDR)iter.map.thr_next;
if (mapp == first)
return 0;
}
}
static void
deactivate_uw_thread (void)
{
remove_thread_event_breakpoints ();
uw_thread_active = 0;
unpush_target (&uw_thread_ops);
}
static ptid_t
thr_to_lwp (ptid_t ptid)
{
struct thread_info *info;
ptid_t lid;
if (!ISTID (ptid))
lid = ptid;
else if (!(info = find_thread_pid (ptid)))
lid = null_ptid;
else if (!info->private->lwpid)
lid = null_ptid;
else
lid = MKLID (PIDGET (ptid), info->private->lwpid);
DBG2((" thr_to_lwp(%s) = %s", dbgpid (pid), dbgpid (lid)));
return lid;
}
static int
find_thread_lwp_callback (struct thread_info *tp, void *data)
{
int lwpid = (int)data;
if (!ISTID (tp->ptid))
return 0;
if (!tp->private->stable)
return 0;
if (lwpid != tp->private->lwpid)
return 0;
return 1;
}
static struct thread_info *
find_thread_lwp (int lwpid)
{
return iterate_over_threads (find_thread_lwp_callback, (void *)lwpid);
}
static ptid_t
lwp_to_thr (ptid_t ptid)
{
struct thread_info *info;
int lwpid;
ptid_t tid = ptid;
if (ISTID (ptid))
goto done;
if (!(lwpid = LIDGET (ptid)))
goto done;
if (!(info = find_thread_lwp (lwpid)))
goto done;
tid = MKTID (PIDGET (ptid), info->private->thrid);
done:
DBG2((ISTID (tid) ? NULL : "lwp_to_thr: no thr for %s", dbgpid (ptid)));
return tid;
}
static void
thr_infpid (void *unused)
{
ptid_t ptid = lwp_to_thr (inferior_ptid);
DBG2((" inferior_ptid from procfs: %s => %s",
dbgpid (inferior_ptid), dbgpid (ptid)));
inferior_ptid = ptid;
}
static int
lwp_infpid (void)
{
ptid_t ptid = thr_to_lwp (inferior_ptid);
DBG2((" inferior_ptid to procfs: %s => %s",
dbgpid (inferior_ptid), dbgpid (ptid)));
if (ptid_equal (ptid, null_ptid))
return 0;
inferior_ptid = ptid;
infpid_cleanup = make_cleanup (thr_infpid, NULL);
return 1;
}
static void
add_thread_uw (int thrid, int lwpid, CORE_ADDR mapp, ptid_t ptid)
{
struct thread_info *newthread;
if ((newthread = add_thread (ptid)) == NULL)
error ("failed to create new thread structure");
newthread->private = xmalloc (sizeof (struct private_thread_info));
newthread->private->stable = 1;
newthread->private->thrid = thrid;
newthread->private->lwpid = lwpid;
newthread->private->mapp = mapp;
if (target_has_execution)
printf_unfiltered ("[New %s]\n", target_pid_to_str (ptid));
}
static int
notice_thread (iter_t *iter, void *data)
{
int thrid = iter->map.thr_tid;
int lwpid = !iter->map.thr_lwpp ? 0 : iter->lwp.lwp_id;
ptid_t ptid = MKTID (PIDGET (inferior_ptid), thrid);
if (!find_thread_pid (ptid) && (!data || thrid == 1))
add_thread_uw (thrid, lwpid, iter->mapp, ptid);
return 0;
}
static void
notice_threads (void)
{
thread_iter (notice_thread, NULL);
}
static CORE_ADDR
find_main (void)
{
if (!thr_map_main)
{
struct thread_info *info;
thread_iter (notice_thread, (void *)1);
if ((info = find_thread_pid (MKTID (PIDGET (inferior_ptid), 1))))
thr_map_main = info->private->mapp;
}
return thr_map_main;
}
static void
uw_thread_attach (char *args, int from_tty)
{
procfs_ops.to_attach (args, from_tty);
if (uw_thread_active)
thr_infpid (NULL);
}
static void
uw_thread_detach (char *args, int from_tty)
{
deactivate_uw_thread ();
base_ops.to_detach (args, from_tty);
}
static void
uw_thread_resume (ptid_t ptid, int step, enum target_signal signo)
{
if (PIDGET (ptid) > 0)
{
ptid = thr_to_lwp (ptid);
if (ptid_equal (ptid, null_ptid))
ptid = pid_to_ptid (-1);
}
CALL_BASE (base_ops.to_resume (ptid, step, signo));
}
static void
libthread_stub (ptid_t ptid)
{
CORE_ADDR sp, mapp, mapp_main;
enum thread_change change;
struct thread_map map;
__lwp_desc_t lwp;
int lwpid;
ptid_t tid = null_ptid;
struct thread_info *info;
if (read_pc_pid (ptid) - DECR_PC_AFTER_BREAK != thr_brk_addr)
return;
sp = read_register_pid (SP_REGNUM, ptid);
if (!base_ops.to_xfer_memory (sp + SP_ARG0, (char *)&mapp,
sizeof (mapp), 0, NULL, &base_ops))
goto err;
if (!base_ops.to_xfer_memory (sp + SP_ARG0 + sizeof (mapp), (char *)&change,
sizeof (change), 0, NULL, &base_ops))
goto err;
mapp_main = find_main ();
switch (change) {
case tc_switch_begin:
if (!mapp)
mapp = mapp_main;
case tc_thread_create:
case tc_thread_exit:
if (!mapp)
break;
if (!read_map (mapp, &map))
goto err;
tid = MKTID (PIDGET (ptid), map.thr_tid);
switch (change) {
case tc_thread_create:
if (!map.thr_lwpp)
lwpid = 0;
else if (!read_lwp ((CORE_ADDR)map.thr_lwpp, &lwp))
goto err;
else
lwpid = lwp.lwp_id;
add_thread_uw (map.thr_tid, lwpid, mapp, tid);
break;
case tc_thread_exit:
printf_unfiltered ("[Exited %s]\n", target_pid_to_str (tid));
delete_thread (tid);
if (ptid_equal (tid, inferior_ptid))
inferior_ptid = ptid;
break;
case tc_switch_begin:
if (switchto_thread)
goto err;
if (!(switchto_thread = find_thread_pid (tid)))
goto err;
switchto_thread->private->stable = 0;
break;
default:
break;
}
break;
case tc_switch_complete:
case tc_cancel_complete:
if (!switchto_thread)
goto err;
if (change == tc_switch_complete)
{
if (!read_map (switchto_thread->private->mapp, &map))
goto err;
if (map.thr_lwpp)
{
if (!read_lwp ((CORE_ADDR)map.thr_lwpp, &lwp))
goto err;
if ((info = find_thread_lwp (lwp.lwp_id)))
info->private->lwpid = 0;
switchto_thread->private->lwpid = lwp.lwp_id;
}
}
switchto_thread->private->stable = 1;
switchto_thread = NULL;
break;
case tc_invalid:
case tc_thread_suspend:
case tc_thread_suspend_pending:
case tc_thread_continue:
err:
DBG(("unexpected condition in libthread_stub()"));
break;
}
DBG2(("libthread_stub(%s): %s %s %s", dbgpid (pid), dbgpid (tid),
dbgchange (change), tid ? dbgstate (map.thr_state) : ""));
}
static ptid_t
uw_thread_wait (ptid_t ptid, struct target_waitstatus *status)
{
if (PIDGET (ptid) > 0)
ptid = thr_to_lwp (ptid);
if (PIDGET (ptid) <= 0)
ptid = pid_to_ptid (-1);
CALL_BASE (ptid = base_ops.to_wait (ptid, status));
if (status->kind == TARGET_WAITKIND_STOPPED &&
status->value.sig == TARGET_SIGNAL_TRAP)
libthread_stub (ptid);
return lwp_to_thr (ptid);
}
static void
uw_thread_fetch_registers (int regno)
{
int called;
struct thread_info *info;
struct thread_map map;
TRY_BASE (base_ops.to_fetch_registers (regno), &called);
if (called)
return;
if (!(info = find_thread_pid (inferior_ptid)))
return;
if (!read_map (info->private->mapp, &map))
return;
supply_gregset (&map.thr_ucontext.uc_mcontext.gregs);
supply_fpregset (&map.thr_ucontext.uc_mcontext.fpregs);
}
static void
uw_thread_store_registers (int regno)
{
CALL_BASE (base_ops.to_store_registers (regno));
}
static void
uw_thread_prepare_to_store (void)
{
CALL_BASE (base_ops.to_prepare_to_store ());
}
static void
uw_thread_create_inferior (char *exec_file, char *allargs, char **env)
{
if (uw_thread_active)
deactivate_uw_thread ();
procfs_ops.to_create_inferior (exec_file, allargs, env);
if (uw_thread_active)
{
find_main ();
thr_infpid (NULL);
}
}
static void
uw_thread_kill (void)
{
base_ops.to_kill ();
}
static void
uw_thread_mourn_inferior (void)
{
deactivate_uw_thread ();
base_ops.to_mourn_inferior ();
}
static int
uw_thread_can_run (void)
{
return procfs_suppress_run;
}
static int
uw_thread_alive (ptid_t ptid)
{
if (!ISTID (ptid))
return base_ops.to_thread_alive (ptid);
return in_thread_list (ptid);
}
static void
uw_thread_find_new_threads (void)
{
CALL_BASE (if (base_ops.to_find_new_threads)
base_ops.to_find_new_threads ());
notice_threads ();
}
static char *
uw_thread_pid_to_str (ptid_t ptid)
{
#define FMT "Thread %ld"
static char buf[sizeof (FMT) + 3 * sizeof (long)];
if (!ISTID (ptid))
return procfs_ops.to_pid_to_str (ptid);
sprintf (buf, FMT, TIDGET (ptid));
#undef FMT
return buf;
}
static char *
uw_extra_thread_info (struct thread_info *info)
{
static char buf[80];
struct thread_map map;
__lwp_desc_t lwp;
int lwpid;
char *name;
if (!ISTID (info->ptid))
return NULL;
if (!info->private->stable)
return "switching";
if (!read_map (info->private->mapp, &map))
return NULL;
if (!map.thr_lwpp || !read_lwp ((CORE_ADDR)map.thr_lwpp, &lwp))
lwpid = 0;
else
lwpid = lwp.lwp_id;
switch (map.thr_state) {
case TS_ONPROC: name = "running"; break;
case TS_SLEEP: name = "sleeping"; break;
case TS_RUNNABLE: name = "runnable"; break;
case TS_ZOMBIE: name = "zombie"; break;
case TS_SUSPENDED: name = "suspended"; break;
#ifdef TS_FORK
case TS_FORK: name = "forking"; break;
#endif
default: name = "confused"; break;
}
if (!lwpid)
return name;
sprintf (buf, "%s, LWP %d", name, lwpid);
return buf;
}
static void
libthread_init (void)
{
struct minimal_symbol *ms;
struct thread_debug debug;
CORE_ADDR onp;
struct breakpoint *b;
int one = 1;
if (uw_thread_active)
return;
if (!(ms = lookup_minimal_symbol ("_thr_debug", NULL, NULL)))
return;
if (!(thr_debug_addr = SYMBOL_VALUE_ADDRESS (ms)))
return;
base_ops = current_target;
if (!read_thr_debug (&debug))
return;
if (!debug.thr_map)
return;
push_target (&uw_thread_ops);
uw_thread_active = 1;
if (!target_has_execution)
notice_threads ();
else
{
thr_brk_addr = (CORE_ADDR)debug.thr_brk;
if (!(b = create_thread_event_breakpoint (thr_brk_addr)))
goto err;
onp = (CORE_ADDR)&((struct thread_debug *)thr_debug_addr)->thr_debug_on;
if (!base_ops.to_xfer_memory ((CORE_ADDR)onp, (char *)&one,
sizeof (one), 1, NULL, &base_ops))
{
delete_breakpoint (b);
goto err;
}
thr_map_main = 0;
}
return;
err:
warning ("uw-thread: unable to initialize user-mode thread debugging\n");
deactivate_uw_thread ();
}
static void
uw_thread_new_objfile (struct objfile *objfile)
{
if (objfile)
libthread_init ();
else if (uw_thread_active)
deactivate_uw_thread ();
if (target_new_objfile_chain)
target_new_objfile_chain (objfile);
}
static void
init_uw_thread_ops (void)
{
uw_thread_ops.to_shortname = "unixware-threads";
uw_thread_ops.to_longname = "UnixWare threads and pthread.";
uw_thread_ops.to_doc = "UnixWare threads and pthread support.";
uw_thread_ops.to_attach = uw_thread_attach;
uw_thread_ops.to_detach = uw_thread_detach;
uw_thread_ops.to_resume = uw_thread_resume;
uw_thread_ops.to_wait = uw_thread_wait;
uw_thread_ops.to_fetch_registers = uw_thread_fetch_registers;
uw_thread_ops.to_store_registers = uw_thread_store_registers;
uw_thread_ops.to_prepare_to_store = uw_thread_prepare_to_store;
uw_thread_ops.to_create_inferior = uw_thread_create_inferior;
uw_thread_ops.to_kill = uw_thread_kill;
uw_thread_ops.to_mourn_inferior = uw_thread_mourn_inferior;
uw_thread_ops.to_can_run = uw_thread_can_run;
uw_thread_ops.to_thread_alive = uw_thread_alive;
uw_thread_ops.to_find_new_threads = uw_thread_find_new_threads;
uw_thread_ops.to_pid_to_str = uw_thread_pid_to_str;
uw_thread_ops.to_extra_thread_info = uw_extra_thread_info;
uw_thread_ops.to_stratum = thread_stratum;
uw_thread_ops.to_magic = OPS_MAGIC;
}
void
_initialize_uw_thread (void)
{
init_uw_thread_ops ();
add_target (&uw_thread_ops);
procfs_suppress_run = 1;
target_new_objfile_chain = target_new_objfile_hook;
target_new_objfile_hook = uw_thread_new_objfile;
}