add various debugging info to venti http server: /proc/stacks, /proc/threads, etc. Reference: /n/sources/patch/applied/vdebug Date: Wed Oct 3 17:57:57 CES 2007 Signed-off-by: rsc@swtch.com --- /sys/src/cmd/venti/srv/mkfile Wed Oct 3 17:56:54 2007 +++ /sys/src/cmd/venti/srv/mkfile Wed Oct 3 17:56:54 2007 @@ -13,6 +13,7 @@ dump.$O\ graph.$O\ hdisk.$O\ + hproc.$O\ httpd.$O\ icache.$O\ icachewrite.$O\ @@ -45,6 +46,8 @@ HFILES= dat.h\ fns.h\ stdinc.h\ + /sys/include/venti.h\ + /sys/include/httpd.h\ TARG=\ venti\ --- /sys/src/cmd/venti/srv/fns.h Wed Oct 3 17:56:55 2007 +++ /sys/src/cmd/venti/srv/fns.h Wed Oct 3 17:56:55 2007 @@ -59,6 +59,7 @@ int hdebug(HConnect*); int hdisk(HConnect*); int hnotfound(HConnect*); +int hproc(HConnect*); int hsethtml(HConnect*); int hsettext(HConnect*); int httpdinit(char *address, char *webroot); --- /sys/src/cmd/venti/srv/httpd.c Wed Oct 3 17:56:57 2007 +++ /sys/src/cmd/venti/srv/httpd.c Wed Oct 3 17:56:56 2007 @@ -69,6 +69,7 @@ httpdobj("/emptydcache", hdcacheempty); httpdobj("/disk", hdisk); httpdobj("/debug", hdebug); + httpdobj("/proc/", hproc); if(vtproc(listenproc, address) < 0) return -1; --- /sys/src/cmd/venti/srv/hproc.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/venti/srv/hproc.c Wed Oct 3 17:58:07 2007 @@ -0,0 +1,659 @@ +#include "stdinc.h" +#include +#include +#include +#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; +}; + +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; insegs; 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); + } +} + +#define ADDR ulong + +static void +printlocals(Map *map, Symbol *fn, ADDR fp) +{ + int i; + ulong 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 (get4(map, fp-s.value, &w) > 0) + dprint("\t%-10s %10#lux %ld\n", buf, w, w); + else + dprint("\t%-10s ?\n", buf); + } +} + +static void +printparams(Map *map, Symbol *fn, ADDR fp) +{ + int i; + Symbol s; + ulong 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 (get4(map, fp+s.value, &w) > 0) + dprint("%s=%#lux", s.name, w); + } +} + +static void +printsource(ADDR dot) +{ + char str[100]; + + if (fileline(str, sizeof str, dot)) + dprint("%s", str); +} + + +/* + * callback on stack trace + */ +static ulong 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("+%#lx ", nextpc - sym->value); + printsource(nextpc); + dprint("\n"); + printlocals(map, sym, sp); + nextpc = pc; +} + +static void +stacktracepcsp(Map *m, ulong pc, ulong 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=%#lux sp=%#lux\n", pc, sp); +} + +static void +stacktrace(Map *m) +{ + ulong pc, sp; + + if(get4(m, offsetof(Ureg, pc), &pc) < 0){ + dprint("get4 pc: %r"); + return; + } + if(get4(m, offsetof(Ureg, sp), &sp) < 0){ + dprint("get4 sp: %r"); + return; + } + stacktracepcsp(m, pc, sp); +} + +static ulong +star(ulong addr) +{ + ulong x; + static int warned; + + if(addr == 0) + return 0; + + if(debug.map == nil){ + if(!warned++) + dprint("no debug.map\n"); + return 0; + } + if(get4(debug.map, addr, &x) < 0){ + dprint("get4 %#lux (pid=%d): %r\n", addr, debug.pid); + return 0; + } + return x; +} + +static ulong +resolvev(char *name) +{ + Symbol s; + + if(lookup(nil, name, &s) == 0) + return 0; + return s.value; +} + +static ulong +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 ulong 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; + if(strprefix(buf, "/sys/src/libthread/") == 0) + return; + threadpc = pc; +} + +static char* +threadstkline(ulong t) +{ + ulong pc, sp; + static char buf[500]; + + if(FIELD(Thread, t, state) == Running){ + get4(debug.map, offsetof(Ureg, pc), &pc); + get4(debug.map, offsetof(Ureg, sp), &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(ulong p) +{ + dprint("p=(Proc)%#lux 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(ulong 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(ulong t) +{ + static char buf[4096]; + Fmt fmt; + int s; + + fmtbufinit(&fmt, buf, sizeof buf); + + fmtprint(&fmt, "t=(Thread)%#lux ", 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(ulong t) +{ + dprint("%s\n", threadfmt(t)); +} + +static void +threadapply(ulong p, void (*fn)(ulong)) +{ + int oldpid, pid; + ulong 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(ulong t) +{ + dprint("\t"); + thread(t); +} + +static void +pthreads(ulong p) +{ + threadapply(p, pthreads1); +} + +static void +lproc(ulong p) +{ + proc(p); + pthreads(p); +} + +static void +procapply(void (*fn)(ulong)) +{ + ulong proc; + ulong 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(ulong t) +{ + ulong 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(ulong t) +{ + dprint("\t"); + thread(t); + threadstack(t); + dprint("\n"); +} + +static void +pstacks(ulong 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*); + static char buf[65536]; + Fmt fmt; + + 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; +}