Subversion Repositories planix.SVN

Rev

Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

#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;
}