123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674 |
- #include "stdinc.h"
- #include <bio.h>
- #include <mach.h>
- #include <ureg.h>
- #include "/sys/src/libthread/threadimpl.h"
- #include "dat.h"
- #include "fns.h"
- typedef struct Ureg Ureg;
- typedef struct Debug Debug;
- struct Debug
- {
- int textfd;
- QLock lock;
- Fhdr fhdr;
- Map *map;
- Fmt *fmt;
- int pid;
- char *stkprefix;
- int pcoff;
- int spoff;
- };
- static Debug debug = { -1 };
- static int
- text(int pid)
- {
- int fd;
- char buf[100];
- if(debug.textfd >= 0){
- close(debug.textfd);
- debug.textfd = -1;
- }
- memset(&debug.fhdr, 0, sizeof debug.fhdr);
-
- snprint(buf, sizeof buf, "#p/%d/text", pid);
- fd = open(buf, OREAD);
- if(fd < 0)
- return -1;
- if(crackhdr(fd, &debug.fhdr) < 0){
- close(fd);
- return -1;
- }
- if(syminit(fd, &debug.fhdr) < 0){
- memset(&debug.fhdr, 0, sizeof debug.fhdr);
- close(fd);
- return -1;
- }
- debug.textfd = fd;
- machbytype(debug.fhdr.type);
- return 0;
- }
- static void
- unmap(Map *m)
- {
- int i;
-
- for(i=0; i<m->nsegs; i++)
- if(m->seg[i].inuse)
- close(m->seg[i].fd);
- free(m);
- }
- static Map*
- map(int pid)
- {
- int mem;
- char buf[100];
- Map *m;
-
- snprint(buf, sizeof buf, "#p/%d/mem", pid);
- mem = open(buf, OREAD);
- if(mem < 0)
- return nil;
- m = attachproc(pid, 0, mem, &debug.fhdr);
- if(m == 0){
- close(mem);
- return nil;
- }
-
- if(debug.map)
- unmap(debug.map);
- debug.map = m;
- debug.pid = pid;
- return m;
- }
- static void
- dprint(char *fmt, ...)
- {
- va_list arg;
-
- va_start(arg, fmt);
- fmtvprint(debug.fmt, fmt, arg);
- va_end(arg);
- }
- static void
- openfiles(void)
- {
- char buf[4096];
- int fd, n;
-
- snprint(buf, sizeof buf, "#p/%d/fd", getpid());
- if((fd = open(buf, OREAD)) < 0){
- dprint("open %s: %r\n", buf);
- return;
- }
- n = readn(fd, buf, sizeof buf-1);
- close(fd);
- if(n >= 0){
- buf[n] = 0;
- fmtstrcpy(debug.fmt, buf);
- }
- }
- /*
- * dump the raw symbol table
- */
- static void
- printsym(void)
- {
- int i;
- Sym *sp;
- for (i = 0; sp = getsym(i); i++) {
- switch(sp->type) {
- case 't':
- case 'l':
- dprint("%16#llux t %s\n", sp->value, sp->name);
- break;
- case 'T':
- case 'L':
- dprint("%16#llux T %s\n", sp->value, sp->name);
- break;
- case 'D':
- case 'd':
- case 'B':
- case 'b':
- case 'a':
- case 'p':
- case 'm':
- dprint("%16#llux %c %s\n", sp->value, sp->type, sp->name);
- break;
- default:
- break;
- }
- }
- }
- static void
- printmap(char *s, Map *map)
- {
- int i;
- if (!map)
- return;
- dprint("%s\n", s);
- for (i = 0; i < map->nsegs; i++) {
- if (map->seg[i].inuse)
- dprint("%-16s %-16#llux %-16#llux %-16#llux\n",
- map->seg[i].name, map->seg[i].b,
- map->seg[i].e, map->seg[i].f);
- }
- }
- static void
- printlocals(Map *map, Symbol *fn, uintptr fp)
- {
- int i;
- uintptr w;
- Symbol s;
- char buf[100];
- s = *fn;
- for (i = 0; localsym(&s, i); i++) {
- if (s.class != CAUTO)
- continue;
- snprint(buf, sizeof buf, "%s%s/", debug.stkprefix, s.name);
- if (geta(map, fp - s.value, (uvlong*)&w) > 0)
- dprint("\t%-10s %10#p %ld\n", buf, w, w);
- else
- dprint("\t%-10s ?\n", buf);
- }
- }
- static void
- printparams(Map *map, Symbol *fn, uintptr fp)
- {
- int i;
- Symbol s;
- uintptr w;
- int first = 0;
- fp += mach->szaddr; /* skip saved pc */
- s = *fn;
- for (i = 0; localsym(&s, i); i++) {
- if (s.class != CPARAM)
- continue;
- if (first++)
- dprint(", ");
- if (geta(map, fp + s.value, (uvlong *)&w) > 0)
- dprint("%s=%#p", s.name, w);
- }
- }
- static void
- printsource(uintptr dot)
- {
- char str[100];
- if (fileline(str, sizeof str, dot))
- dprint("%s", str);
- }
- /*
- * callback on stack trace
- */
- static uintptr nextpc;
- static void
- ptrace(Map *map, uvlong pc, uvlong sp, Symbol *sym)
- {
- if(nextpc == 0)
- nextpc = sym->value;
- if(debug.stkprefix == nil)
- debug.stkprefix = "";
- dprint("%s%s(", debug.stkprefix, sym->name);
- printparams(map, sym, sp);
- dprint(")");
- if(nextpc != sym->value)
- dprint("+%#llux ", nextpc - sym->value);
- printsource(nextpc);
- dprint("\n");
- printlocals(map, sym, sp);
- nextpc = pc;
- }
- static void
- stacktracepcsp(Map *m, uintptr pc, uintptr sp)
- {
- nextpc = 0;
- if(machdata->ctrace==nil)
- dprint("no machdata->ctrace\n");
- else if(machdata->ctrace(m, pc, sp, 0, ptrace) <= 0)
- dprint("no stack frame: pc=%#p sp=%#p\n", pc, sp);
- }
- static void
- ureginit(void)
- {
- Reglist *r;
- for(r = mach->reglist; r->rname; r++)
- if (strcmp(r->rname, "PC") == 0)
- debug.pcoff = r->roffs;
- else if (strcmp(r->rname, "SP") == 0)
- debug.spoff = r->roffs;
- }
- static void
- stacktrace(Map *m)
- {
- uintptr pc, sp;
-
- if(geta(m, debug.pcoff, (uvlong *)&pc) < 0){
- dprint("geta pc: %r");
- return;
- }
- if(geta(m, debug.spoff, (uvlong *)&sp) < 0){
- dprint("geta sp: %r");
- return;
- }
- stacktracepcsp(m, pc, sp);
- }
- static uintptr
- star(uintptr addr)
- {
- uintptr x;
- static int warned;
- if(addr == 0)
- return 0;
- if(debug.map == nil){
- if(!warned++)
- dprint("no debug.map\n");
- return 0;
- }
- if(geta(debug.map, addr, (uvlong *)&x) < 0){
- dprint("geta %#p (pid=%d): %r\n", addr, debug.pid);
- return 0;
- }
- return x;
- }
- static uintptr
- resolvev(char *name)
- {
- Symbol s;
- if(lookup(nil, name, &s) == 0)
- return 0;
- return s.value;
- }
- static uintptr
- resolvef(char *name)
- {
- Symbol s;
- if(lookup(name, nil, &s) == 0)
- return 0;
- return s.value;
- }
- #define FADDR(type, p, name) ((p) + offsetof(type, name))
- #define FIELD(type, p, name) star(FADDR(type, p, name))
- static uintptr threadpc;
- static int
- strprefix(char *big, char *pre)
- {
- return strncmp(big, pre, strlen(pre));
- }
- static void
- tptrace(Map *map, uvlong pc, uvlong sp, Symbol *sym)
- {
- char buf[512];
- USED(map);
- USED(sym);
- USED(sp);
- if(threadpc != 0)
- return;
- if(!fileline(buf, sizeof buf, pc))
- return;
- if(strprefix(buf, "/sys/src/libc/") == 0)
- return;
- if(strprefix(buf, "/sys/src/libthread/") == 0)
- return;
- threadpc = pc;
- }
- static char*
- threadstkline(uintptr t)
- {
- uintptr pc, sp;
- static char buf[500];
- if(FIELD(Thread, t, state) == Running){
- geta(debug.map, debug.pcoff, (uvlong *)&pc);
- geta(debug.map, debug.spoff, (uvlong *)&sp);
- }else{
- // pc = FIELD(Thread, t, sched[JMPBUFPC]);
- pc = resolvef("longjmp");
- sp = FIELD(Thread, t, sched[JMPBUFSP]);
- }
- if(machdata->ctrace == nil)
- return "";
- threadpc = 0;
- machdata->ctrace(debug.map, pc, sp, 0, tptrace);
- if(!fileline(buf, sizeof buf, threadpc))
- buf[0] = 0;
- return buf;
- }
- static void
- proc(uintptr p)
- {
- dprint("p=(Proc)%#p pid %d ", p, FIELD(Proc, p, pid));
- if(FIELD(Proc, p, thread) == 0)
- dprint(" Sched\n");
- else
- dprint(" Running\n");
- }
- static void
- fmtbufinit(Fmt *f, char *buf, int len)
- {
- memset(f, 0, sizeof *f);
- f->runes = 0;
- f->start = buf;
- f->to = buf;
- f->stop = buf + len - 1;
- f->flush = nil;
- f->farg = nil;
- f->nfmt = 0;
- }
- static char*
- fmtbufflush(Fmt *f)
- {
- *(char*)f->to = 0;
- return (char*)f->start;
- }
- static char*
- debugstr(uintptr s)
- {
- static char buf[4096];
- char *p, *e;
-
- p = buf;
- e = buf+sizeof buf - 1;
- while(p < e){
- if(get1(debug.map, s++, (uchar*)p, 1) < 0)
- break;
- if(*p == 0)
- break;
- p++;
- }
- *p = 0;
- return buf;
- }
- static char*
- threadfmt(uintptr t)
- {
- static char buf[4096];
- Fmt fmt;
- int s;
- fmtbufinit(&fmt, buf, sizeof buf);
-
- fmtprint(&fmt, "t=(Thread)%#p ", t);
- switch(s = FIELD(Thread, t, state)){
- case Running:
- fmtprint(&fmt, " Running ");
- break;
- case Ready:
- fmtprint(&fmt, " Ready ");
- break;
- case Rendezvous:
- fmtprint(&fmt, " Rendez ");
- break;
- default:
- fmtprint(&fmt, " bad state %d ", s);
- break;
- }
-
- fmtprint(&fmt, "%s", threadstkline(t));
-
- if(FIELD(Thread, t, moribund) == 1)
- fmtprint(&fmt, " Moribund");
- if(s = FIELD(Thread, t, cmdname)){
- fmtprint(&fmt, " [%s]", debugstr(s));
- }
- fmtbufflush(&fmt);
- return buf;
- }
- static void
- thread(uintptr t)
- {
- dprint("%s\n", threadfmt(t));
- }
- static void
- threadapply(uintptr p, void (*fn)(uintptr))
- {
- int oldpid, pid;
- uintptr tq, t;
-
- oldpid = debug.pid;
- pid = FIELD(Proc, p, pid);
- if(map(pid) == nil)
- return;
- tq = FADDR(Proc, p, threads);
- t = FIELD(Tqueue, tq, head);
- while(t != 0){
- fn(t);
- t = FIELD(Thread, t, nextt);
- }
- map(oldpid);
- }
- static void
- pthreads1(uintptr t)
- {
- dprint("\t");
- thread(t);
- }
- static void
- pthreads(uintptr p)
- {
- threadapply(p, pthreads1);
- }
- static void
- lproc(uintptr p)
- {
- proc(p);
- pthreads(p);
- }
- static void
- procapply(void (*fn)(uintptr))
- {
- uintptr proc, pq;
-
- pq = resolvev("_threadpq");
- if(pq == 0){
- dprint("no thread run queue\n");
- return;
- }
- proc = FIELD(Pqueue, pq, head);
- while(proc){
- fn(proc);
- proc = FIELD(Proc, proc, next);
- }
- }
- static void
- threads(HConnect *c)
- {
- USED(c);
- procapply(lproc);
- }
- static void
- procs(HConnect *c)
- {
- USED(c);
- procapply(proc);
- }
- static void
- threadstack(uintptr t)
- {
- uintptr pc, sp;
- if(FIELD(Thread, t, state) == Running){
- stacktrace(debug.map);
- }else{
- // pc = FIELD(Thread, t, sched[JMPBUFPC]);
- pc = resolvef("longjmp");
- sp = FIELD(Thread, t, sched[JMPBUFSP]);
- stacktracepcsp(debug.map, pc, sp);
- }
- }
- static void
- tstacks(uintptr t)
- {
- dprint("\t");
- thread(t);
- threadstack(t);
- dprint("\n");
- }
- static void
- pstacks(uintptr p)
- {
- proc(p);
- threadapply(p, tstacks);
- }
- static void
- stacks(HConnect *c)
- {
- USED(c);
- debug.stkprefix = "\t\t";
- procapply(pstacks);
- debug.stkprefix = "";
- }
- static void
- symbols(HConnect *c)
- {
- USED(c);
- printsym();
- }
- static void
- segments(HConnect *c)
- {
- USED(c);
- printmap("segments", debug.map);
- }
- static void
- fds(HConnect *c)
- {
- USED(c);
- openfiles();
- }
- static void
- all(HConnect *c)
- {
- dprint("/proc/segment\n");
- segments(c);
- dprint("\n/proc/fd\n");
- fds(c);
- dprint("\n/proc/procs\n");
- procs(c);
- dprint("\n/proc/threads\n");
- threads(c);
- dprint("\n/proc/stacks\n");
- stacks(c);
- dprint("\n# /proc/symbols\n");
- // symbols(c);
- }
- int
- hproc(HConnect *c)
- {
- void (*fn)(HConnect*);
- Fmt fmt;
- static int beenhere;
- static char buf[65536];
- if (!beenhere) {
- beenhere = 1;
- ureginit();
- }
- if(strcmp(c->req.uri, "/proc/all") == 0)
- fn = all;
- else if(strcmp(c->req.uri, "/proc/segment") == 0)
- fn = segments;
- else if(strcmp(c->req.uri, "/proc/fd") == 0)
- fn = fds;
- else if(strcmp(c->req.uri, "/proc/procs") == 0)
- fn = procs;
- else if(strcmp(c->req.uri, "/proc/threads") == 0)
- fn = threads;
- else if(strcmp(c->req.uri, "/proc/stacks") == 0)
- fn = stacks;
- else if(strcmp(c->req.uri, "/proc/symbols") == 0)
- fn = symbols;
- else
- return hnotfound(c);
- if(hsettext(c) < 0)
- return -1;
- if(!canqlock(&debug.lock)){
- hprint(&c->hout, "debugger is busy\n");
- return 0;
- }
- if(debug.textfd < 0){
- if(text(getpid()) < 0){
- hprint(&c->hout, "cannot attach self text: %r\n");
- goto out;
- }
- }
- if(map(getpid()) == nil){
- hprint(&c->hout, "cannot map self: %r\n");
- goto out;
- }
- fmtbufinit(&fmt, buf, sizeof buf);
- debug.fmt = &fmt;
- fn(c);
- hprint(&c->hout, "%s\n", fmtbufflush(&fmt));
- debug.fmt = nil;
- out:
- qunlock(&debug.lock);
- return 0;
- }
|