|  | #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 | 
|  | } | 
|  |  | 
|  | */ |