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