|
@@ -0,0 +1,659 @@
|
|
|
+#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;
|
|
|
+}
|