@record frame { pc, fp }; @define isfnentry(pc) { @local a; a = getbytes((char*)pc, 4); if(a == "\x55\x48\x89\xe5") // push %rbp; mov %rsp,%rbp return 1; else return 0; } @define frameat(pc, sp, fp) { if(isfnentry(pc)) return frame(((void**)sp)[0], (void*)fp); else return frame(((void**)fp)[1], ((void**)fp)[0]); } @define frameprev(f) { @local fp; fp = f.fp; return frame(*((void**)fp+1), *((void**)fp+0)); } @define fnargs(a) { @local args, sym, fn, ns, i, m, rv, t; ns = nsof(a); sym = ns.lookaddr(a); rv = []; if(sym == nil) return rv; fn = symtype(sym); if(!isfunc(fn)) return rv; args = params(fn); m = length(args); if(m == 1 && isvoid(paramtype(args[0]))) return rv; for(i = 0; i < m; i++){ t = paramtype(args[i]); if(issu(t)) error("frameargs unimplemented for aggregates"); append(rv, t); } return rv; } @define fnret(a) { @local sym, fn, ns, t; ns = nsof(a); sym = ns.lookaddr(a); if(sym == nil) return nil; fn = symtype(sym); if(!isfunc(fn)) return nil; t = rettype(fn); if(t == @typeof(void)) return nil; return t; } @define frameargs(f) { @local args, sym, fp, fn, ns, i, m, o, rv, t, p; fp = (void**)f.fp; ns = nsof(f.pc); sym = ns.lookaddr(f.pc); rv = []; if(sym == nil) return rv; fn = symtype(sym); if(!isfunc(fn)) return rv; args = params(fn); m = length(args); if(m == 1 && isvoid(paramtype(args[0]))) return rv; o = 2; for(i = 0; i < m; i++){ t = paramtype(args[i]); if(issu(t)) error("frameargs unimplemented for aggregates"); p = {mkctype_ptr(t, nsptr(ns))}(fp+o); o += sizeof(t)/sizeof(p); append(rv, *p); } return rv; } @define foreachframe(ctl, ns, fn) { @local as, regs, dom, pc, fp, sp, f; as = ctl.mem(); regs = ctl.reg(); dom = mkdom(ns, as); pc = (void*){dom}regs->rip; fp = (void*){dom}regs->rbp; sp = (void*){dom}regs->rsp; f = frame(pc, fp); fn(f); if(pc == 0 || fp == 0) return nil; f = frameat(pc, sp, fp); while(f.fp != 0 && f.pc != 0){ fn(f); f = frameprev(f); } return nil; } @define fmtframe(f) { return sprintfa("%016x\t%y", f.pc, f.pc); } @define fmtstack(ctl, ns) { @local rv; rv = ""; foreachframe(ctl, ns, @lambda(f){ rv += sprintfa("\t%s\n", fmtframe(f)); }); return rv; } @define lbrk(dom, spec, fn) { @local file, line, ss, ctl, pc; ss = split(spec, ":"); if(length(ss) != 2) error("invalid source line specification: %a", spec); file = ss[0]; line = strton(ss[1]); ctl = dom.ctl(); pc = dom.lookpc(file, line); if(pc == nil) error("cannot find location for %a", spec); return ctl.xtrap(pc, fn); } @define brk(a, fn) { @local dom, ctl, ts, args, i, m, regs, rs; dom = domof(a); ctl = dom.ctl(); ts = fnargs(a); m = length(ts); if(m > 6) error("xtrap: function has more than six parameters"); return ctl.xtrap(a, @lambda(ctl){ regs = ctl.reg(); dom = mkdom(dom.ns, ctl.mem()); args = [ctl]; // first six args in rdi, rsi, rdx, rcx, r8, r9 // float args in xmm* rs = [ {dom}regs->rdi, {dom}regs->rsi, {dom}regs->rdx, {dom}regs->rcx, {dom}regs->r8, {dom}regs->r9 ]; for(i = 0; i < m; i++) append(args, {ts[i]}rs[i]); return apply(fn, args); }); } @define rbrk(a, fn) { @local dom, ns, rt; dom = domof(a); ns = dom.ns; rt = fnret(a); brk(a, @lambda(ectl, arg ...){ @local dom, regs, sp, rid, rax; regs = ectl.reg(); dom = mkdom(ns, ectl.mem()); sp = (void**){dom}regs->rsp; rid = ectl.xtrap(*sp, @lambda(rctl){ ectl.trapdel(rid); regs = rctl.reg(); dom = mkdom(ns, rctl.mem()); rax = regs->rax; if(rt != nil) push(arg, {rt}{dom}rax); push(arg, rctl); return apply(fn, arg); }); }); } @define brbrk(a, in) { @local dom, ns, rt; dom = domof(a); ns = dom.ns; rt = fnret(a); brk(a, @lambda(ectl, arg ...){ @local dom, regs, sp, rid, rax, out; regs = ectl.reg(); dom = mkdom(ns, ectl.mem()); push(arg, ectl); out = apply(in, arg); if(out == nil) return; pop(arg); sp = (void**){dom}regs->rsp; rid = ectl.xtrap(*sp, @lambda(rctl){ ectl.trapdel(rid); regs = rctl.reg(); dom = mkdom(ns, rctl.mem()); rax = regs->rax; if(rt != nil) push(arg, {rt}{dom}rax); push(arg, rctl); return apply(out, arg); }); }); }