mirror of
https://github.com/9fans/plan9port.git
synced 2025-01-15 11:20:03 +00:00
269 lines
5.5 KiB
C
269 lines
5.5 KiB
C
#include <u.h>
|
|
#include <thread_db.h>
|
|
#include <sys/ptrace.h>
|
|
#include <errno.h>
|
|
#include <sys/procfs.h> /* psaddr_t */
|
|
#include <libc.h>
|
|
#include <mach.h>
|
|
#include "ureg386.h"
|
|
|
|
int td_get_allthreads(td_thragent_t *ta, td_thrhandle_t **pall);
|
|
|
|
static char *tderrstr[] =
|
|
{
|
|
[TD_OK] "no error",
|
|
[TD_ERR] "some error",
|
|
[TD_NOTHR] "no matching thread found",
|
|
[TD_NOSV] "no matching synchronization handle found",
|
|
[TD_NOLWP] "no matching light-weight process found",
|
|
[TD_BADPH] "invalid process handle",
|
|
[TD_BADTH] "invalid thread handle",
|
|
[TD_BADSH] "invalid synchronization handle",
|
|
[TD_BADTA] "invalid thread agent",
|
|
[TD_BADKEY] "invalid key",
|
|
[TD_NOMSG] "no event available",
|
|
[TD_NOFPREGS] "no floating-point register content available",
|
|
[TD_NOLIBTHREAD] "application not linked with thread library",
|
|
[TD_NOEVENT] "requested event is not supported",
|
|
[TD_NOEVENT] "requested event is not supported",
|
|
[TD_NOCAPAB] "capability not available",
|
|
[TD_DBERR] "internal debug library error",
|
|
[TD_NOAPLIC] "operation is not applicable",
|
|
[TD_NOTSD] "no thread-specific data available",
|
|
[TD_MALLOC] "out of memory",
|
|
[TD_PARTIALREG] "not entire register set was read or written",
|
|
[TD_NOXREGS] "X register set not available for given threads",
|
|
[TD_TLSDEFER] "thread has not yet allocated TLS for given module",
|
|
[TD_VERSION] "version mismatch twixt libpthread and libthread_db",
|
|
[TD_NOTLS] "there is no TLS segment in the given module",
|
|
};
|
|
|
|
static char*
|
|
terr(int e)
|
|
{
|
|
static char buf[50];
|
|
|
|
if(e < 0 || e >= nelem(tderrstr) || tderrstr[e] == nil){
|
|
snprint(buf, sizeof buf, "thread err %d", e);
|
|
return buf;
|
|
}
|
|
return tderrstr[e];
|
|
}
|
|
|
|
void
|
|
usage(void)
|
|
{
|
|
fprint(2, "usage: t pid\n");
|
|
exits("usage");
|
|
}
|
|
|
|
#define STRINGSZ 128
|
|
|
|
/*
|
|
* print the value of dot as file:line
|
|
*/
|
|
void
|
|
printsource(long dot)
|
|
{
|
|
char str[STRINGSZ];
|
|
|
|
if (fileline(dot, str, STRINGSZ) >= 0)
|
|
print("%s", str);
|
|
}
|
|
|
|
void
|
|
printlocals(Symbol *fn, Regs *regs)
|
|
{
|
|
int i;
|
|
u32int v;
|
|
Symbol s;
|
|
|
|
for (i = 0; indexlsym(fn, i, &s)>=0; i++) {
|
|
if (s.class != CAUTO)
|
|
continue;
|
|
if(lget4(cormap, regs, s.loc, &v) >= 0)
|
|
print("\t%s.%s/\t%#lux\n", fn->name, s.name, v);
|
|
else
|
|
print("\t%s.%s/\t?\n", fn->name, s.name);
|
|
}
|
|
}
|
|
|
|
void
|
|
printparams(Symbol *fn, Regs *regs)
|
|
{
|
|
int i;
|
|
Symbol s;
|
|
u32int v;
|
|
int first = 0;
|
|
ulong pc, sp, bp;
|
|
|
|
if(0) print("pc=%lux sp=%lux bp=%lux ",
|
|
(rget(regs, "PC", &pc), pc),
|
|
(rget(regs, "SP", &sp), sp),
|
|
(rget(regs, "BP", &bp), bp));
|
|
for (i = 0; indexlsym(fn, i, &s)>=0; i++) {
|
|
if (s.class != CPARAM)
|
|
continue;
|
|
if (first++)
|
|
print(", ");
|
|
if(0) print("(%d.%s.%ux.%x)", s.loc.type, s.loc.reg, s.loc.addr, s.loc.offset);
|
|
if(lget4(cormap, regs, s.loc, &v) >= 0)
|
|
print("%s=%#lux", s.name, v);
|
|
else
|
|
print("%s=?", s.name);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* callback on stack trace
|
|
*/
|
|
static int
|
|
xtrace(Map *map, Regs *regs, ulong pc, ulong nextpc, Symbol *sym, int depth)
|
|
{
|
|
char buf[512];
|
|
|
|
USED(map);
|
|
print("\t");
|
|
if(sym){
|
|
print("%s(", sym->name);
|
|
printparams(sym, regs);
|
|
print(")+0x%ux ", pc-sym->loc.addr);
|
|
}else
|
|
print("%#lux ", pc);
|
|
printsource(pc);
|
|
|
|
print(" called from ");
|
|
symoff(buf, 512, nextpc, CTEXT);
|
|
print("%s ", buf);
|
|
/* printsource(nextpc); */
|
|
print("\n");
|
|
if(sym)
|
|
printlocals(sym, regs);
|
|
return depth<40;
|
|
}
|
|
|
|
void
|
|
main(int argc, char **argv)
|
|
{
|
|
struct ps_prochandle p;
|
|
prgregset_t regs;
|
|
int e;
|
|
td_thragent_t *ta;
|
|
td_thrhandle_t *ts;
|
|
td_thrinfo_t info;
|
|
int i, n;
|
|
Ureg *u;
|
|
UregRegs r;
|
|
|
|
ARGBEGIN{
|
|
default:
|
|
usage();
|
|
}ARGEND
|
|
|
|
attachargs(argc, argv, OREAD);
|
|
attachdynamic();
|
|
|
|
// if(!corpid && !corhdr)
|
|
// sysfatal("could not attach to process");
|
|
//
|
|
p.pid = corpid;
|
|
if((e = td_ta_new(&p, &ta)) != TD_OK)
|
|
sysfatal("td_ta_new: %s", terr(e));
|
|
if((e = td_ta_get_nthreads(ta, &n)) != TD_OK)
|
|
sysfatal("td_ta_get_nthreads: %s", terr(e));
|
|
print("%d threads\n", n);
|
|
|
|
if((n = td_get_allthreads(ta, &ts)) < 0)
|
|
sysfatal("td_get_allthreads: %r");
|
|
print("%d threads - regs = %p\n", n, regs);
|
|
for(i=0; i<n; i++){
|
|
if((e = td_thr_get_info(&ts[i], &info)) != TD_OK)
|
|
sysfatal("td_thr_get_info: %s", terr(e));
|
|
print("%d: startfunc=%lux stkbase=%lux pc=%lux sp=%lux lid=%d\n",
|
|
i, info.ti_startfunc, info.ti_stkbase, info.ti_pc, info.ti_sp, info.ti_lid);
|
|
if((e = td_thr_getgregs(&ts[i], regs)) != TD_OK)
|
|
sysfatal("td_thr_getregs: %s", terr(e));
|
|
print("%d: pc=%lux sp=%lux gs=%lux\n", i, regs[12], regs[15], regs[10]);
|
|
if((u = _linux2ureg386((UregLinux386*)regs)) == nil)
|
|
sysfatal("%r");
|
|
r.r.rw = _uregrw;
|
|
r.ureg = (uchar*)u;
|
|
stacktrace(cormap, &r.r, xtrace);
|
|
}
|
|
exits(0);
|
|
}
|
|
|
|
typedef struct AllThread AllThread;
|
|
struct AllThread
|
|
{
|
|
td_thrhandle_t *a;
|
|
int n;
|
|
int err;
|
|
};
|
|
|
|
static int
|
|
thritercb(const td_thrhandle_t *th, void *cb)
|
|
{
|
|
td_thrhandle_t **p;
|
|
AllThread *a;
|
|
int n;
|
|
|
|
a = cb;
|
|
if((a->n&(a->n-1)) == 0){
|
|
if(a->n == 0)
|
|
n = 1;
|
|
else
|
|
n = a->n<<1;
|
|
if((p = realloc(a->a, n*sizeof a->a[0])) == 0){
|
|
a->err = -1;
|
|
return -1; /* stop iteration */
|
|
}
|
|
a->a = p;
|
|
}
|
|
a->a[a->n++] = *th;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
td_get_allthreads(td_thragent_t *ta, td_thrhandle_t **pall)
|
|
{
|
|
int e;
|
|
AllThread a;
|
|
|
|
a.a = nil;
|
|
a.n = 0;
|
|
a.err = 0;
|
|
if((e = td_ta_thr_iter(ta, thritercb, &a,
|
|
TD_THR_ANY_STATE,
|
|
TD_THR_LOWEST_PRIORITY,
|
|
TD_SIGNO_MASK,
|
|
TD_THR_ANY_USER_FLAGS)) != TD_OK){
|
|
werrstr("%s", terr(e));
|
|
return -1;
|
|
}
|
|
|
|
if(a.err){
|
|
free(a.a);
|
|
return -1;
|
|
}
|
|
|
|
*pall = a.a;
|
|
return a.n;
|
|
}
|
|
|
|
/*
|
|
td_err_e td_ta_map_id2thr(const td_thragent_t *ta_p, thread_t tid,td_thrhandle_t *th_p);
|
|
*/
|
|
|
|
/*
|
|
int
|
|
threadregs(int tid, Regs **rp)
|
|
{
|
|
check pid
|
|
look up tid (td_ta_map_id2thr)
|
|
create Regs with thr handle inside
|
|
rw function calls thr_getregs and then
|
|
pulls out the desired register
|
|
}
|
|
|
|
*/
|