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