summaryrefslogtreecommitdiff
path: root/appl/lib/debug.b
diff options
context:
space:
mode:
authorCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
committerCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
commit37da2899f40661e3e9631e497da8dc59b971cbd0 (patch)
treecbc6d4680e347d906f5fa7fca73214418741df72 /appl/lib/debug.b
parent54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff)
20060303a
Diffstat (limited to 'appl/lib/debug.b')
-rw-r--r--appl/lib/debug.b1493
1 files changed, 1493 insertions, 0 deletions
diff --git a/appl/lib/debug.b b/appl/lib/debug.b
new file mode 100644
index 00000000..09273dc7
--- /dev/null
+++ b/appl/lib/debug.b
@@ -0,0 +1,1493 @@
+implement Debug;
+
+include "sys.m";
+sys: Sys;
+sprint, FD: import sys;
+
+include "string.m";
+str: String;
+
+include "draw.m";
+
+include "debug.m";
+
+include "dis.m";
+ dism: Dis;
+
+Command: module
+{
+ init: fn(ctxt: ref Draw->Context, argv: list of string);
+};
+
+Spin: adt
+{
+ spin: int;
+ pspin: int;
+};
+
+SrcState: adt
+{
+ files: array of string;
+ lastf: int;
+ lastl: int;
+ vers: int; # version number
+ # 11 => more source states
+};
+
+typenames := array[] of {
+ Terror => "error",
+ Tid => "id",
+ Tadt => "adt",
+ Tadtpick => "adtpick",
+ Tarray => "array",
+ Tbig => "big",
+ Tbyte => "byte",
+ Tchan => "chan",
+ Treal => "real",
+ Tfn => "fn",
+ Targ => "arg",
+ Tlocal => "local",
+ Tglobal => "global",
+ Tint => "int",
+ Tlist => "list",
+ Tmodule => "module",
+ Tnil => "nil",
+ Tnone => "none",
+ Tref => "ref",
+ Tstring => "string",
+ Ttuple => "tuple",
+ Tend => "end",
+ Targs => "args",
+ Tslice => "slice",
+ Tpoly => "poly",
+};
+
+tnone: ref Type;
+tnil: ref Type;
+tint: ref Type;
+tbyte: ref Type;
+tbig: ref Type;
+treal: ref Type;
+tstring: ref Type;
+tpoly: ref Type;
+
+IBY2WD: con 4;
+IBY2LG: con 8;
+H: con int 16rffffffff;
+
+ModHash: con 32;
+SymHash: con 32;
+mods:= array[ModHash] of list of ref Module;
+syms:= array[SymHash] of list of ref Sym;
+
+sblpath := array[] of
+{
+ ("/dis/", "/appl/cmd/"),
+ ("/dis/", "/appl/"),
+};
+
+init(): int
+{
+ sys = load Sys Sys->PATH;
+ str = load String String->PATH;
+ if(sys == nil || str == nil)
+ return 0;
+ tnone = ref Type(nil, Tnone, 0, "", nil, nil, nil);
+ tnil = ref Type(nil, Tnil, IBY2WD, "nil", nil, nil, nil);
+ tint = ref Type(nil, Tint, IBY2WD, "int", nil, nil, nil);
+ tbyte = ref Type(nil, Tbyte, 1, "byte", nil, nil, nil);
+ tbig = ref Type(nil, Tbig, IBY2LG, "big", nil, nil, nil);
+ treal = ref Type(nil, Treal, IBY2LG, "real", nil, nil, nil);
+ tstring = ref Type(nil, Tstring, IBY2WD, "string", nil, nil, nil);
+ tpoly = ref Type(nil, Tpoly, IBY2WD, "polymorphic", nil, nil, nil);
+ return 1;
+}
+
+prog(pid: int): (ref Prog, string)
+{
+ spid := string pid;
+ h := sys->open("/prog/"+spid+"/heap", sys->ORDWR);
+ if(h == nil)
+ return (nil, sprint("can't open heap file: %r"));
+ c := sys->open("/prog/"+spid+"/ctl", sys->OWRITE);
+ if(c == nil)
+ return (nil, sprint("can't open ctl file: %r"));
+ d := sys->open("/prog/"+spid+"/dbgctl", sys->ORDWR);
+ if(d == nil)
+ return (nil, sprint("can't open debug ctl file: %r"));
+ s := sys->open("/prog/"+spid+"/stack", sys->OREAD);
+ if(s == nil)
+ return (nil, sprint("can't open stack file: %r"));
+ return (ref Prog(pid, h, c, d, s), "");
+}
+
+startprog(dis, dir: string, ctxt: ref Draw->Context, argv: list of string): (ref Prog, string)
+{
+ c := load Command dis;
+ if(c == nil)
+ return (nil, "module not loaded");
+
+ ack := chan of int;
+ spin := ref Spin(1, 1);
+ end := chan of int;
+ spawn execer(ack, dir, c, ctxt, argv, spin, end);
+ kid := <-ack;
+
+ fd := sys->open("/prog/"+string kid+"/dbgctl", sys->ORDWR);
+ if(fd == nil){
+ spin.spin = -1;
+ <- end;
+ return (nil, sprint("can't open debug ctl file: %r"));
+ }
+ done := chan of string;
+ spawn stepper(done, fd, spin);
+
+wait: for(;;){
+ alt{
+ <-ack =>
+ sys->sleep(0);
+ err := <-done =>
+ if(err != ""){
+ <- end;
+ return(nil, err);
+ }
+ break wait;
+ }
+ }
+
+ b := array[20] of byte;
+ n := sys->read(fd, b, len b);
+ if(n <= 0){
+ <- end;
+ return(nil, sprint("%r"));
+ }
+ msg := string b[:n];
+ if(!str->prefix("new ", msg)){
+ <- end;
+ return (nil, msg);
+ }
+
+ kid = int msg[len "new ":];
+
+ # clean up the execer slave
+ b = array of byte "start";
+ sys->write(fd, b, len b);
+
+ <- end;
+ return prog(kid);
+}
+
+stepper(done: chan of string, ctl: ref FD, spin: ref Spin)
+{
+ b := array of byte "step1";
+ while(spin.pspin){
+ if(sys->write(ctl, b, len b) != len b)
+ done <-= sprint("can't start new thread: %r");
+ spin.spin = 0;
+ }
+ done <-= "";
+}
+
+execer(ack: chan of int, dir: string, c: Command, ctxt: ref Draw->Context, args: list of string, spin: ref Spin, end: chan of int)
+{
+ pid := sys->pctl(Sys->NEWPGRP|Sys->FORKNS|Sys->NEWFD, 0::1::2::nil);
+ sys->chdir(dir);
+ while(spin.spin == 1)
+ ack <-= pid;
+ if(spin.spin == -1){
+ end <-= 0;
+ exit;
+ }
+ spawn c->init(ctxt, args);
+ spin.pspin = 0;
+ end <-= 0;
+ exit;
+}
+
+# format of each line is
+# fp pc mp prog compiled path
+# fp, pc, mp, and prog are %.8lux
+# compile is or 1
+# path is a string
+Prog.stack(p: self ref Prog): (array of ref Exp, string)
+{
+ buf := array[8192] of byte;
+ sys->seek(p.stk, big 0, 0);
+ n := sys->read(p.stk, buf, len buf - 1);
+ if(n < 0)
+ return (nil, sprint("can't read stack file: %r"));
+ buf[n] = byte 0;
+
+ t := 0;
+ nf := 0;
+ for(s := 0; s < n; s = t+1){
+ t = strchr(buf, s, '\n');
+ if(buf[t] != byte '\n' || t-s < 40)
+ continue;
+ nf++;
+ }
+
+ e := array[nf] of ref Exp;
+ nf = 0;
+ for(s = 0; s < n; s = t+1){
+ t = strchr(buf, s, '\n');
+ if(buf[t] != byte '\n' || t-s < 40)
+ continue;
+ e[nf] = ref Exp("unknown fn",
+ hex(buf[s+0:s+8]),
+ hex(buf[s+9:s+17]),
+ mkmod(hex(buf[s+18:s+26]), hex(buf[s+27:s+35]), buf[36] != byte '0', string buf[s+38:t]),
+ p,
+ nil);
+ nf++;
+ }
+
+ return (e, "");
+}
+
+Prog.step(p: self ref Prog, how: int): string
+{
+ (stack, nil) := p.stack();
+ if(stack == nil)
+ return "can't find initial pc";
+ src := stack[0].srcstr();
+ stmt := ftostmt(stack[0]);
+
+ if(stack[0].m.sym == nil)
+ how = -1;
+
+ buf := array of byte("step1");
+ if(how == StepOut)
+ buf = array of byte("toret");
+ while(sys->write(p.dbgctl, buf, len buf) == len buf){
+ (stk, err) := p.stack();
+ if(err != nil)
+ return "";
+ case how{
+ StepExp =>
+ if(src != stk[0].srcstr())
+ return "";
+ StepStmt =>
+ if(stmt != ftostmt(stk[0]))
+ return "";
+ if(stk[0].offset != stack[0].offset)
+ return "";
+ StepOut =>
+ if(returned(stack, stk))
+ return "";
+ StepOver =>
+ if(stk[0].offset == stack[0].offset){
+ if(stmt != ftostmt(stk[0]))
+ return "";
+ buf = array of byte("step1");
+ break;
+ }
+ if(returned(stack, stk))
+ return "";
+ buf = array of byte("toret");
+ * =>
+ return "";
+ }
+ }
+ return sprint("%r");
+}
+
+Prog.stop(p: self ref Prog): string
+{
+ return dbgctl(p, "stop");
+}
+
+Prog.unstop(p: self ref Prog): string
+{
+ return dbgctl(p, "unstop");
+}
+
+Prog.grab(p: self ref Prog): string
+{
+ return dbgctl(p, "step0");
+}
+
+Prog.start(p: self ref Prog): string
+{
+ return dbgctl(p, "start");
+}
+
+Prog.cont(p: self ref Prog): string
+{
+ return dbgctl(p, "cont");
+}
+
+dbgctl(p: ref Prog, msg: string): string
+{
+ b := array of byte msg;
+ while(sys->write(p.dbgctl, b, len b) != len b)
+ return sprint("%r");
+ return "";
+}
+
+returned(old, new: array of ref Exp): int
+{
+ n := len old;
+ if(n > len new)
+ return 1;
+ return 0;
+}
+
+Prog.setbpt(p: self ref Prog, dis: string, pc:int): string
+{
+ b := array of byte("bpt set "+dis+" "+string pc);
+ if(sys->write(p.dbgctl, b, len b) != len b)
+ return sprint("can't set breakpoint: %r");
+ return "";
+}
+
+Prog.delbpt(p: self ref Prog, dis: string, pc:int): string
+{
+ b := array of byte("bpt del "+dis+" "+string pc);
+ if(sys->write(p.dbgctl, b, len b) != len b)
+ return sprint("can't del breakpoint: %r");
+ return "";
+}
+
+Prog.kill(p: self ref Prog): string
+{
+ b := array of byte "kill";
+ if(sys->write(p.ctl, b, len b) != len b)
+ return sprint("can't kill process: %r");
+ return "";
+}
+
+Prog.event(p: self ref Prog): string
+{
+ b := array[100] of byte;
+ n := sys->read(p.dbgctl, b, len b);
+ if(n < 0)
+ return sprint("error: %r");
+ return string b[:n];
+}
+
+ftostmt(e: ref Exp): int
+{
+ m := e.m;
+ if(!m.comp && m.sym != nil && e.pc < len m.sym.srcstmt)
+ return m.sym.srcstmt[e.pc];
+ return -1;
+}
+
+Exp.srcstr(e: self ref Exp): string
+{
+ m := e.m;
+ if(!m.comp && m.sym != nil && e.pc < len m.sym.src){
+ src := m.sym.src[e.pc];
+ ss := src.start.file+":"+string src.start.line+"."+string src.start.pos+", ";
+ if(src.stop.file != src.start.file)
+ ss += src.stop.file+":"+string src.stop.line+".";
+ else if(src.stop.line != src.start.line)
+ ss += string src.stop.line+".";
+ return ss+string src.stop.pos;
+ }
+ return sprint("Module %s PC %d", e.m.path, e.pc);
+}
+
+Exp.findsym(e: self ref Exp): string
+{
+ m := e.m;
+ if(m.comp)
+ return "compiled module";
+ if(m.sym != nil){
+ n := e.pc;
+ fns := m.sym.fns;
+ for(i := 0; i < len fns; i++){
+ if(n >= fns[i].offset && n < fns[i].stoppc){
+ e.name = fns[i].name;
+ e.id = fns[i];
+ return "";
+ }
+ }
+ return "pc out of bounds";
+ }
+ return "no symbol file";
+}
+
+Exp.src(e: self ref Exp): ref Src
+{
+ m := e.m;
+ if(e.id == nil || m.sym == nil)
+ return nil;
+ src := e.id.src;
+ if(src != nil)
+ return src;
+ if(e.id.t.kind == Tfn && !m.comp && e.pc < len m.sym.src && e.pc >= 0)
+ return m.sym.src[e.pc];
+ return nil;
+}
+
+Type.getkind(t: self ref Type, sym: ref Sym): int
+{
+ if(t == nil)
+ return -1;
+ if(t.kind == Tid)
+ return sym.adts[int t.name].getkind(sym);
+ return t.kind;
+}
+
+Type.text(t: self ref Type, sym: ref Sym): string
+{
+ if (t == nil)
+ return "no type";
+ s := typenames[t.kind];
+ case t.kind {
+ Tadt or
+ Tadtpick or
+ Tmodule =>
+ s = t.name;
+ Tid =>
+ return sym.adts[int t.name].text(sym);
+ Tarray or
+ Tlist or
+ Tchan or
+ Tslice =>
+ s += " of " + t.Of.text(sym);
+ Tref =>
+ s += " " + t.Of.text(sym);
+ Tfn =>
+ s += "(";
+ for(i := 0; i < len t.ids; i++)
+ s += t.ids[i].name + ": " + t.ids[i].t.text(sym);
+ s += "): " + t.Of.text(sym);
+ Ttuple or
+ Tlocal or
+ Tglobal or
+ Targ =>
+ if(t.kind == Ttuple)
+ s = "";
+ s += "(";
+ for (i := 0; i < len t.ids; i++) {
+ s += t.ids[i].t.text(sym);
+ if (i < len t.ids - 1)
+ s += ", ";
+ }
+ s += ")";
+ }
+ return s;
+}
+
+Exp.typename(e: self ref Exp): string
+{
+ if (e.id == nil)
+ return "no info";
+ return e.id.t.text(e.m.sym);
+}
+
+Exp.kind(e: self ref Exp): int
+{
+ if(e.id == nil)
+ return -1;
+ return e.id.t.getkind(e.m.sym);
+}
+
+EXPLISTMAX : con 32; # what's a good value for this ?
+
+Exp.expand(e: self ref Exp): array of ref Exp
+{
+ if(e.id == nil)
+ return nil;
+
+ t := e.id.t;
+ if(t.kind == Tid)
+ t = e.m.sym.adts[int t.name];
+
+ off := e.offset;
+ ids := t.ids;
+ case t.kind{
+ Tadt or Tfn or Targ or Tlocal or Ttuple =>
+ break;
+ Tadtpick =>
+ break;
+ Tglobal =>
+ ids = e.m.sym.vars;
+ off = e.m.data;
+ Tmodule =>
+ (s, err) := pdata(e.p, off, "M");
+ if(s == "nil" || err != "")
+ return nil;
+ off = hex(array of byte s);
+ Tref =>
+ (s, err) := pdata(e.p, off, "P");
+ if(s == "nil" || err != "")
+ return nil;
+ off = hex(array of byte s);
+ et := t.Of;
+ if(et.kind == Tid)
+ et = e.m.sym.adts[int et.name];
+ ids = et.ids;
+ if(et.kind == Tadtpick){
+ (s, err) = pdata(e.p, off, "W");
+ tg := int s;
+ if(tg < 0 || tg > len et.tags || err != "" )
+ return nil;
+ k := array[1 + len ids + len et.tags[tg].ids] of ref Exp;
+ k[0] = ref Exp(et.tags[tg].name, off+0, e.pc, e.m, e.p, ref Id(et.src, et.tags[tg].name, 0, 0, tint));
+ x := 1;
+ for(i := 0; i < len ids; i++){
+ id := ids[i];
+ k[i+x] = ref Exp(id.name, off+id.offset, e.pc, e.m, e.p, id);
+ }
+ x += len ids;
+ ids = et.tags[tg].ids;
+ for(i = 0; i < len ids; i++){
+ id := ids[i];
+ k[i+x] = ref Exp(id.name, off+id.offset, e.pc, e.m, e.p, id);
+ }
+ return k;
+ }
+ Tlist =>
+ (s, err) := pdata(e.p, off, "L");
+ if(err != "")
+ return nil;
+ (tloff, hdoff) := str->splitl(s, ".");
+ hdoff = hdoff[1:];
+ k := array[2] of ref Exp;
+ k[0] = ref Exp("hd", hex(array of byte hdoff), e.pc, e.m, e.p, ref Id(nil, "hd", H, H, t.Of));
+ k[1] = ref Exp("tl", hex(array of byte tloff), e.pc, e.m, e.p, ref Id(nil, "tl", H, H, t));
+ return k;
+ Tarray =>
+ (s, err) := pdata(e.p, e.offset, "A");
+ if(s == "nil")
+ return nil;
+ (sn, sa) := str->splitl(s, ".");
+ n := int sn;
+ if(sa == "" || n <= 0)
+ return nil;
+ (off, nil) = str->toint(sa[1:], 16);
+ et := t.Of;
+ if(et.kind == Tid)
+ et = e.m.sym.adts[int et.name];
+ esize := et.size;
+ if (n <= EXPLISTMAX || EXPLISTMAX == 0) {
+ k := array[n] of ref Exp;
+ for(i := 0; i < n; i++){
+ name := string i;
+ k[i] = ref Exp(name, off+i*esize, e.pc, e.m, e.p, ref Id(nil, name, H, H, et));
+ }
+ return k;
+ }
+ else {
+ # slice it
+ (p, q, r) := partition(n, EXPLISTMAX);
+ lb := 0;
+ k := array[p] of ref Exp;
+ st := ref Type(et.src, Tslice, 0, nil, et, nil, nil);
+ for (i := 0; i < p; i++){
+ ub := lb+q-1;
+ if (--r >= 0)
+ ub++;
+ name := string lb + ".." + string ub;
+ k[i] = ref Exp(name, off+lb*esize, e.pc, e.m, e.p, ref Id(nil, name, H, H, st));
+ lb = ub+1;
+ }
+ return k;
+ }
+ Tslice =>
+ (lb, ub) := bounds(e.name);
+ if (lb > ub)
+ return nil;
+ n := ub-lb+1;
+ et := t.Of;
+ if(et.kind == Tid)
+ et = e.m.sym.adts[int et.name];
+ esize := et.size;
+ if (n <= EXPLISTMAX || EXPLISTMAX == 0) {
+ k := array[n] of ref Exp;
+ for(i := 0; i < n; i++){
+ name := string (i+lb);
+ k[i] = ref Exp(name, off+i*esize, e.pc, e.m, e.p, ref Id(nil, name, H, H, et));
+ }
+ return k;
+ }
+ else {
+ # slice it again
+ (p, q, r) := partition(n, EXPLISTMAX);
+ lb0 := lb;
+ k := array[p] of ref Exp;
+ st := ref Type(et.src, Tslice, 0, nil, et, nil, nil);
+ for (i := 0; i < p; i++){
+ ub = lb+q-1;
+ if (--r >= 0)
+ ub++;
+ name := string lb + ".." + string ub;
+ k[i] = ref Exp(name, off+(lb-lb0)*esize, e.pc, e.m, e.p, ref Id(nil, name, H, H, st));
+ lb = ub+1;
+ }
+ return k;
+ }
+ Tchan =>
+ (s, err) := pdata(e.p, e.offset, "c");
+ if(s == "nil")
+ return nil;
+ (sn, sa) := str->splitl(s, ".");
+ n := int sn;
+ if(sa == "" || n <= 0)
+ return nil;
+ (off, nil) = str->toint(sa[1:], 16);
+ (nil, sa) = str->splitl(sa[1:], ".");
+ (sn, sa) = str->splitl(sa[1:], ".");
+ f := int sn;
+ sz := int sa[1:];
+ et := t.Of;
+ if(et.kind == Tid)
+ et = e.m.sym.adts[int et.name];
+ esize := et.size;
+ k := array[sz] of ref Exp;
+ for(i := 0; i < sz; i++){
+ name := string i;
+ j := (f+i)%n;
+ k[i] = ref Exp(name, off+j*esize, e.pc, e.m, e.p, ref Id(nil, name, H, H, et));
+ }
+ return k;
+ * =>
+ return nil;
+ }
+ k := array[len ids] of ref Exp;
+ for(i := 0; i < len k; i++){
+ id := ids[i];
+ k[i] = ref Exp(id.name, off+id.offset, e.pc, e.m, e.p, id);
+ }
+ return k;
+}
+
+Exp.val(e: self ref Exp): (string, int)
+{
+ if(e.id == nil)
+ return (e.m.path+" unknown fn", 0);
+ t := e.id.t;
+ if(t.kind == Tid)
+ t = e.m.sym.adts[int t.name];
+
+ w := 0;
+ s := "";
+ err := "";
+ p := e.p;
+ case t.kind{
+ Tfn =>
+ if(t.ids != nil)
+ w = 1;
+ src := e.m.sym.src[e.pc];
+ ss := src.start.file+":"+string src.start.line+"."+string src.start.pos+", ";
+ if(src.stop.file != src.start.file)
+ ss += src.stop.file+":"+string src.stop.line+".";
+ else if(src.stop.line != src.start.line)
+ ss += string src.stop.line+".";
+ return (ss+string src.stop.pos, w);
+ Targ or Tlocal or Tglobal or Tadtpick or Ttuple =>
+ return ("", 1);
+ Tadt =>
+ return ("#" + string e.offset, 1);
+ Tnil =>
+ s = "nil";
+ Tbyte =>
+ (s, err) = pdata(p, e.offset, "B");
+ Tint =>
+ (s, err) = pdata(p, e.offset, "W");
+ Tbig =>
+ (s, err) = pdata(p, e.offset, "V");
+ Treal =>
+ (s, err) = pdata(p, e.offset, "R");
+ Tarray =>
+ (s, err) = pdata(p, e.offset, "A");
+ if(s == "nil")
+ break;
+ (n, a) := str->splitl(s, ".");
+ if(a == "")
+ return ("", 0);
+ s = "["+n+"] @"+a[1:];
+ w = 1;
+ Tslice =>
+ (lb, ub) := bounds(e.name);
+ s = sys->sprint("[:%d] @ %x", ub-lb+1, e.offset);
+ w = 1;
+ Tstring =>
+ n : int;
+ (n, s, err) = pstring(p, e.offset);
+ if(err != "")
+ return ("", 0);
+ for(i := 0; i < len s; i++)
+ if(s[i] == '\n')
+ s[i] = '\u008a';
+ s = "["+string n+"] \""+s+"\"";
+ Tref or Tlist or Tmodule or Tpoly=>
+ (s, err) = pdata(p, e.offset, "P");
+ if(s == "nil")
+ break;
+ s = "@" + s;
+ w = 1;
+ Tchan =>
+ (s, err) = pdata(p, e.offset, "c");
+ if(s == "nil")
+ break;
+ (n, a) := str->splitl(s, ".");
+ if(a == "")
+ return ("", 0);
+ if(n == "0"){
+ s = "@" + a[1:];
+ w = 0;
+ }
+ else{
+ (a, nil) = str->splitl(a[1:], ".");
+ s = "["+n+"] @"+a;
+ w = 1;
+ }
+ }
+ if(err != "")
+ return ("", 0);
+ return (s, w);
+}
+
+Sym.srctopc(s: self ref Sym, src: ref Src): int
+{
+ srcs := s.src;
+ line := src.start.line;
+ pos := src.start.pos;
+ (nil, file) := str->splitr(src.start.file, "/");
+ backup := -1;
+ delta := 80;
+ for(i := 0; i < len srcs; i++){
+ ss := srcs[i];
+ if(ss.start.file != file)
+ continue;
+ if(ss.start.line <= line && ss.start.pos <= pos
+ && ss.stop.line >= line && ss.stop.pos >= pos)
+ return i;
+ d := ss.start.line - line;
+ if(d >= 0 && d < delta){
+ delta = d;
+ backup = i;
+ }
+ }
+ return backup;
+}
+
+Sym.pctosrc(s: self ref Sym, pc: int): ref Src
+{
+ if(pc < 0 || pc >= len s.src)
+ return nil;
+ return s.src[pc];
+}
+
+sym(sbl: string): (ref Sym, string)
+{
+ h := 0;
+ for(i := 0; i < len sbl; i++)
+ h = (h << 1) + sbl[i];
+ h &= SymHash - 1;
+ for(sl := syms[h]; sl != nil; sl = tl sl){
+ s := hd sl;
+ if(sbl == s.path)
+ return (s, "");
+ }
+ (sy, err) := loadsyms(sbl);
+ if(err != "")
+ return (nil, err);
+ syms[h] = sy :: syms[h];
+ return (sy, "");
+}
+
+Module.addsym(m: self ref Module, sym: ref Sym)
+{
+ m.sym = sym;
+}
+
+Module.sbl(m: self ref Module): string
+{
+ if(m.sym != nil)
+ return m.sym.path;
+ return "";
+}
+
+Module.dis(m: self ref Module): string
+{
+ return m.path;
+}
+
+findsbl(dis: string): string
+{
+ n := len dis;
+ if(n <= 4 || dis[n-4: n] != ".dis")
+ dis += ".dis";
+ if(dism == nil){
+ dism = load Dis Dis->PATH;
+ if(dism != nil)
+ dism->init();
+ }
+ if(dism != nil && (b := dism->src(dis)) != nil){
+ n = len b;
+ if(n > 2 && b[n-2: n] == ".b"){
+ sbl := b[0: n-2] + ".sbl";
+ if(sys->open(sbl, Sys->OREAD) != nil)
+ return sbl;
+ }
+ }
+ return nil;
+}
+
+Module.stdsym(m: self ref Module)
+{
+ if(m.sym != nil)
+ return;
+ if((sbl := findsbl(m.path)) != nil){
+ (m.sym, nil) = sym(sbl);
+ return;
+ }
+ sbl = m.path;
+ n := len sbl;
+ if(n > 4 && sbl[n-4:n] == ".dis")
+ sbl = sbl[:n-4]+".sbl";
+ else
+ sbl = sbl+".sbl";
+ path := sbl;
+ fd := sys->open(sbl, sys->OREAD);
+ for(i := 0; fd == nil && i < len sblpath; i++){
+ (dis, src) := sblpath[i];
+ nd := len dis;
+ if(len sbl > nd && sbl[:nd] == dis){
+ path = src + sbl[nd:];
+ fd = sys->open(path, sys->OREAD);
+ }
+ }
+ if(fd == nil)
+ return;
+ (m.sym, nil) = sym(path);
+}
+
+mkmod(data, code, comp: int, dis: string): ref Module
+{
+ h := 0;
+ for(i := 0; i < len dis; i++)
+ h = (h << 1) + dis[i];
+ h &= ModHash - 1;
+ sym : ref Sym;
+ for(ml := mods[h]; ml != nil; ml = tl ml){
+ m := hd ml;
+ if(m.path == dis && m.code == code && m.comp == comp){
+ sym = m.sym;
+ if(m.data == data)
+ return m;
+ }
+ }
+ m := ref Module(dis, code, data, comp, sym);
+ mods[h] = m :: mods[h];
+ return m;
+}
+
+pdata(p: ref Prog, a: int, fmt: string): (string, string)
+{
+ b := array of byte sprint("0x%ux.%s1", a, fmt);
+ if(sys->write(p.heap, b, len b) != len b)
+ return ("", sprint("can't write heap: %r"));
+
+ buf := array[64] of byte;
+ sys->seek(p.heap, big 0, 0);
+ n := sys->read(p.heap, buf, len buf);
+ if(n <= 1)
+ return ("", sprint("can't read heap: %r"));
+ return (string buf[:n-1], "");
+}
+
+pstring0(p: ref Prog, a: int, blen: int): (int, string, string)
+{
+ b := array of byte sprint("0x%ux.C1", a);
+ if(sys->write(p.heap, b, len b) != len b)
+ return (-1, "", sprint("can't write heap: %r"));
+
+ buf := array[blen] of byte;
+ sys->seek(p.heap, big 0, 0);
+ n := sys->read(p.heap, buf, len buf-1);
+ if(n <= 1)
+ return (-1, "", sprint("can't read heap: %r"));
+ buf[n] = byte 0;
+ m := strchr(buf, 0, '.');
+ if(buf[m++] != byte '.')
+ m = 0;
+ return (int string buf[0:m], string buf[m:n], "");
+}
+
+pstring(p: ref Prog, a: int): (int, string, string)
+{
+ m, n: int;
+ s, err: string;
+
+ m = 64;
+ for(;;){
+ (n, s, err) = pstring0(p, a, m);
+ if(err != "" || n <= len s)
+ break;
+ m *= 2;
+ }
+ return (n, s, err);
+}
+
+Prog.status(p: self ref Prog): (int, string, string, string)
+{
+ fd := sys->open(sprint("/prog/%d/status", p.id), sys->OREAD);
+ if(fd == nil)
+ return (-1, "", sprint("can't open status file: %r"), "");
+ buf := array[256] of byte;
+ n := sys->read(fd, buf, len buf);
+ if(n <= 0)
+ return (-1, "", sprint("can't read status file: %r"), "");
+ (ni, info) := sys->tokenize(string buf[:n], " \t");
+ if(ni != 6 && ni != 7)
+ return (-1, "", "can't parse status file", "");
+ info = tl info;
+ if(ni == 6)
+ return (int hd info, hd tl info, hd tl tl info, hd tl tl tl tl info);
+ return (int hd info, hd tl info, hd tl tl tl info, hd tl tl tl tl tl info);
+}
+
+loadsyms(sbl: string): (ref Sym, string)
+{
+ fd := sys->open(sbl, sys->OREAD);
+ if(fd == nil)
+ return (nil, sprint("Can't open symbol file '%s': %r", sbl));
+
+ (ok, dir) := sys->fstat(fd);
+ if(ok < 0)
+ return (nil, sprint("Can't read symbol file '%s': %r", sbl));
+ n := int dir.length;
+ buf := array[n+1] of byte;
+ if(sys->read(fd, buf, n) != n)
+ return (nil, sprint("Can't read symbol file '%s': %r", sbl));
+ fd = nil;
+ buf[n] = byte 0;
+
+ s := ref Sym;
+ s.path = sbl;
+
+ n = strchr(buf, 0, '\n');
+ vers := 0;
+ if(string buf[:n] == "limbo .sbl 1.")
+ vers = 10;
+ else if(string buf[:n] == "limbo .sbl 1.1")
+ vers = 11;
+ else if(string buf[:n] == "limbo .sbl 2.0")
+ vers = 20;
+ else if(string buf[:n] == "limbo .sbl 2.1")
+ vers = 21;
+ else
+ return (nil, "Symbol file "+sbl+" out of date");
+ o := n += 1;
+ n = strchr(buf, o, '\n');
+ if(buf[n] != byte '\n')
+ return (nil, "Corrupted symbol file "+sbl);
+ s.name = string buf[o:n++];
+ ss := ref SrcState(nil, 0, 0, vers);
+ err : string;
+ if(n >= 0){
+ err = "file";
+ n = debugfiles(ss, buf, n);
+ }
+ if(n >= 0){
+ err = "pc";
+ n = debugpc(ss, s, buf, n);
+ }
+ if(n >= 0){
+ err = "types";
+ n = debugtys(ss, s, buf, n);
+ }
+ if(n >= 0){
+ err = "fn";
+ n = debugfns(ss, s, buf, n);
+ }
+ vs: array of ref Id;
+ if(n >= 0){
+ err = "global";
+ (vs, n) = debugid(ss, buf, n);
+ }
+ if(n < 0)
+ return (nil, "Corrupted "+err+" symbol table in "+sbl);
+ s.vars = vs;
+ return (s, "");
+}
+
+#
+# parse a source location
+# format[file:][line.]pos,[file:][line.]pos' '
+#
+debugsrc(ss: ref SrcState, buf: array of byte, p: int): (ref Src, int)
+{
+ n: int;
+ src: ref Src;
+
+ (n, p) = strtoi(buf, p);
+ if(buf[p] == byte ':'){
+ ss.lastf = n;
+ (n, p) = strtoi(buf, p + 1);
+ }
+ if(buf[p] == byte '.'){
+ ss.lastl = n;
+ (n, p) = strtoi(buf, p + 1);
+ }
+ if(buf[p++] != byte ',' || ss.lastf >= len ss.files || ss.lastf < 0)
+ return (nil, -1);
+ src = ref Src;
+ src.start.file = ss.files[ss.lastf];
+ src.start.line = ss.lastl;
+ src.start.pos = n;
+
+ (n, p) = strtoi(buf, p);
+ if(buf[p] == byte ':'){
+ ss.lastf = n;
+ (n, p) = strtoi(buf, p+1);
+ }
+ if(buf[p] == byte '.'){
+ ss.lastl = n;
+ (n, p) = strtoi(buf, p + 1);
+ }
+ if(buf[p++] != byte ' ' || ss.lastf >= len ss.files || ss.lastf < 0)
+ return (nil, -1);
+ src.stop.file = ss.files[ss.lastf];
+ src.stop.line = ss.lastl;
+ src.stop.pos = n;
+ return (src, p);
+}
+
+#
+# parse the file table
+# item format: file: string
+#
+debugfiles(ss: ref SrcState, buf: array of byte, p: int): int
+{
+ n, q: int;
+
+ (n, p) = strtoi(buf, p);
+ if(buf[p++] != byte '\n')
+ return -1;
+ ss.files = array[n] of string;
+ for(i := 0; i < n; i++){
+ q = strchr(buf, p, '\n');
+ ss.files[i] = string buf[p:q];
+ p = q + 1;
+ }
+ return p;
+}
+
+#
+# parse the pc to source table
+# item format: Source stmt
+#
+debugpc(ss: ref SrcState, s: ref Sym, buf: array of byte, p: int): int
+{
+ ns: int;
+
+ (ns, p) = strtoi(buf, p);
+ if(buf[p++] != byte '\n')
+ return -1;
+ s.src = array[ns] of ref Src;
+ s.srcstmt = array[ns] of int;
+ for(i := 0; i < ns; i++){
+ (s.src[i], p) = debugsrc(ss, buf, p);
+ if(p < 0)
+ return -1;
+ (s.srcstmt[i], p) = strtoi(buf, p);
+ if(buf[p++] != byte '\n')
+ return -1;
+ }
+ return p;
+}
+
+#
+# parse the type table
+# format: linear list of types
+#
+debugtys(ss: ref SrcState, s: ref Sym, buf: array of byte, p: int): int
+{
+ na: int;
+
+ (na, p) = strtoi(buf, p);
+ if(buf[p++] != byte '\n')
+ return -1;
+ s.adts = array[na] of ref Type;
+ adts := s.adts;
+ for(i := 0; i < na; i++){
+ if(ss.vers < 20)
+ (adts[i], p) = debugadt(ss, buf, p);
+ else
+ (adts[i], p) = debugtype(ss, buf, p);
+ if(p < 0)
+ return -1;
+ }
+ return p;
+}
+
+#
+# parse the function table
+# format: pc:name:argids localids rettype
+#
+debugfns(ss: ref SrcState, s: ref Sym, buf: array of byte, p: int): int
+{
+ t: ref Type;
+ args, locals: array of ref Id;
+ nf, pc, q: int;
+
+ (nf, p) = strtoi(buf, p);
+ if(buf[p++] != byte '\n')
+ return -1;
+ s.fns = array[nf] of ref Id;
+ fns := s.fns;
+ for(i := 0; i < nf; i++){
+ (pc, p) = strtoi(buf, p);
+ if(buf[p++] != byte ':')
+ return -2;
+ q = strchr(buf, p, '\n');
+ if(buf[q] != byte '\n')
+ return -3;
+ name := string buf[p:q];
+ (args, p) = debugid(ss, buf, q + 1);
+ if(p == -1)
+ return -4;
+ (locals, p) = debugid(ss, buf, p);
+ if(p == -1)
+ return -5;
+ (t, p) = debugtype(ss, buf, p);
+ if(p == -1)
+ return -6;
+ nk := 1 + (len args != 0) + (len locals != 0);
+ kids := array[nk] of ref Id;
+ nk = 0;
+ if(len locals != 0)
+ kids[nk++] = ref Id(nil, "locals", 0, 0, ref Type(nil, Tlocal, 0, nil, nil, locals, nil));
+ if(len args != 0)
+ kids[nk++] = ref Id(nil, "args", 0, 0, ref Type(nil, Targ, 0, nil, nil, args, nil));
+ kids[nk++] = ref Id(nil, "module", 0, 0, ref Type(nil, Tglobal, 0, nil, nil, nil, nil));
+ args = nil;
+ locals = nil;
+ fns[i] = ref Id(nil, name, pc, 0, ref Type(nil, Tfn, 0, name, t, kids, nil));
+ }
+ for(i = 1; i < nf; i++)
+ fns[i-1].stoppc = fns[i].offset;
+ fns[i-1].stoppc = len s.src;
+ return p;
+}
+
+#
+# parse a list of ids
+# format: offset ':' name ':' src type '\n'
+#
+debugid(ss: ref SrcState, buf: array of byte, p: int): (array of ref Id, int)
+{
+ t: ref Type;
+ off, nd, q, qq, tq: int;
+ src: ref Src;
+
+ (nd, p) = strtoi(buf, p);
+ if(buf[p++] != byte '\n')
+ return (nil, -1);
+ d := array[nd] of ref Id;
+ for(i := 0; i < nd; i++){
+ (off, q) = strtoi(buf, p);
+ if(buf[q++] != byte ':')
+ return (nil, -1);
+ qq = strchr(buf, q, ':');
+ if(buf[qq] != byte ':')
+ return (nil, -1);
+ tq = qq + 1;
+ if(ss.vers > 10){
+ (src, tq) = debugsrc(ss, buf, tq);
+ if(tq < 0)
+ return (nil, -1);
+ }
+ (t, p) = debugtype(ss, buf, tq);
+ if(p == -1 || buf[p++] != byte '\n')
+ return (nil, -1);
+ d[i] = ref Id(src, string buf[q:qq], off, 0, t);
+ }
+ return (d, p);
+}
+
+idlist(a: array of ref Id): list of ref Id
+{
+ n := len a;
+ ids : list of ref Id = nil;
+ while(n-- > 0)
+ ids = a[n] :: ids;
+ return ids;
+}
+
+#
+# parse a type description
+#
+debugtype(ss: ref SrcState, buf: array of byte, p: int): (ref Type, int)
+{
+ t: ref Type;
+ d: array of ref Id;
+ q, k: int;
+ src: ref Src;
+
+ size := 0;
+ case int buf[p++]{
+ '@' =>
+ k = Tid;
+ 'A' =>
+ k = Tarray;
+ size = IBY2WD;
+ 'B' =>
+ return (tbig, p);
+ 'C' => k = Tchan;
+ size = IBY2WD;
+ 'L' =>
+ k = Tlist;
+ size = IBY2WD;
+ 'N' =>
+ return (tnil, p);
+ 'R' =>
+ k = Tref;
+ size = IBY2WD;
+ 'a' =>
+ k = Tadt;
+ if(ss.vers < 20)
+ size = -1;
+ 'b' =>
+ return (tbyte, p);
+ 'f' =>
+ return (treal, p);
+ 'i' =>
+ return (tint, p);
+ 'm' =>
+ k = Tmodule;
+ size = IBY2WD;
+ 'n' =>
+ return (tnone, p);
+ 'p' =>
+ k = Tadtpick;
+ 's' =>
+ return (tstring, p);
+ 't' =>
+ k = Ttuple;
+ size = -1;
+ 'F' =>
+ k = Tfn;
+ size = IBY2WD;
+ 'P' =>
+ return (tpoly, p);
+ * =>
+ k = Terror;
+ }
+
+ if(size == -1){
+ q = strchr(buf, p, '.');
+ if(buf[q] == byte '.'){
+ size = int string buf[p:q];
+ p = q+1;
+ }
+ }
+
+ case k{
+ Tid =>
+ q = strchr(buf, p, '\n');
+ if(buf[q] != byte '\n')
+ return (nil, -1);
+ t = ref Type(nil, Tid, -1, string buf[p:q], nil, nil, nil);
+ p = q + 1;
+ Tadt =>
+ if(ss.vers < 20){
+ q = strchr(buf, p, '\n');
+ if(buf[q] != byte '\n')
+ return (nil, -1);
+ t = ref Type(nil, Tid, size, string buf[p:q], nil, nil, nil);
+ p = q + 1;
+ }else
+ (t, p) = debugadt(ss, buf, p);
+ Tadtpick =>
+ (t, p) = debugadt(ss, buf, p);
+ t.kind = Tadtpick;
+ (t.tags, p) = debugtag(ss, buf, p);
+ Tmodule =>
+ q = strchr(buf, p, '\n');
+ if(buf[q] != byte '\n')
+ return (nil, -1);
+ t = ref Type(nil, k, size, string buf[p:q], nil, nil, nil);
+ p = q + 1;
+ if(ss.vers > 10){
+ (src, p) = debugsrc(ss, buf, p);
+ t.src = src;
+ }
+ if(ss.vers > 20)
+ (t.ids, p) = debugid(ss, buf, p);
+ Tref or Tarray or Tlist or Tchan => # ref, array, list, chan
+ (t, p) = debugtype(ss, buf, p);
+ t = ref Type(nil, k, size, "", t, nil, nil);
+
+ Ttuple => # tuple
+ (d, p) = debugid(ss, buf, p);
+ t = ref Type(nil, k, size, "", nil, d, nil);
+
+ Tfn => # fn
+ (d, p) = debugid(ss, buf, p);
+ (t, p) = debugtype(ss, buf, p);
+ t = ref Type(nil, k, size, "", t, d, nil);
+
+ * =>
+ p = -1;
+ }
+ return (t, p);
+}
+
+#
+# parse an adt type spec
+# format: name ' ' src size '\n' ids
+#
+debugadt(ss: ref SrcState, buf: array of byte, p: int): (ref Type, int)
+{
+ src: ref Src;
+
+ q := strchr(buf, p, ' ');
+ if(buf[q] != byte ' ')
+ return (nil, -1);
+ sq := q + 1;
+ if(ss.vers > 10){
+ (src, sq) = debugsrc(ss, buf, sq);
+ if(sq < 0)
+ return (nil, -1);
+ }
+ qq := strchr(buf, sq, '\n');
+ if(buf[qq] != byte '\n')
+ return (nil, -1);
+ (d, pp) := debugid(ss, buf, qq + 1);
+ if(pp == -1)
+ return (nil, -1);
+ t := ref Type(src, Tadt, int string buf[sq:qq], string buf[p:q], nil, d, nil);
+ return (t, pp);
+}
+
+#
+# parse a list of tags
+# format:
+# name ':' src size '\n' ids
+# or
+# name ':' src '\n'
+#
+debugtag(ss: ref SrcState, buf: array of byte, p: int): (array of ref Type, int)
+{
+ d: array of ref Id;
+ ntg, q, pp, np: int;
+ src: ref Src;
+
+ (ntg, p) = strtoi(buf, p);
+ if(buf[p++] != byte '\n')
+ return (nil, -1);
+ tg := array[ntg] of ref Type;
+ for(i := 0; i < ntg; i++){
+ pp = strchr(buf, p, ':');
+ if(buf[pp] != byte ':')
+ return (nil, -1);
+ q = pp + 1;
+ (src, q) = debugsrc(ss, buf, q);
+ if(q < 0)
+ return (nil, -1);
+ if(buf[q] == byte '\n'){
+ np = q + 1;
+ if(i <= 0)
+ return (nil, -1);
+ tg[i] = ref Type(src, Tadt, tg[i-1].size, string buf[p:pp], nil, tg[i-1].ids, nil);
+ }else{
+ np = strchr(buf, q, '\n');
+ if(buf[np] != byte '\n')
+ return (nil, -1);
+ size := int string buf[q:np];
+ (d, np) = debugid(ss, buf, np+1);
+ if(np == -1)
+ return (nil, -1);
+ tg[i] = ref Type(src, Tadt, size, string buf[p:pp], nil, d, nil);
+ }
+ p = np;
+ }
+ return (tg, p);
+}
+
+strchr(a: array of byte, p, c: int): int
+{
+ bc := byte c;
+ while((b := a[p]) != byte 0 && b != bc)
+ p++;
+ return p;
+}
+
+strtoi(a: array of byte, start: int): (int, int)
+{
+ p := start;
+ for(; c := int a[p]; p++){
+ case c{
+ ' ' or '\t' or '\n' or '\r' =>
+ continue;
+ }
+ break;
+ }
+
+ # sign
+ neg := c == '-';
+ if(neg || c == '+')
+ p++;
+
+ # digits
+ n := 0;
+ nn := 0;
+ ndig := 0;
+ over := 0;
+ for(; c = int a[p]; p++){
+ if(c < '0' || c > '9')
+ break;
+ ndig++;
+ nn = n * 10 + (c - '0');
+ if(nn < n)
+ over = 1;
+ n = nn;
+ }
+ if(ndig == 0)
+ return (0, start);
+ if(neg)
+ n = -n;
+ if(over)
+ if(neg)
+ n = 2147483647;
+ else
+ n = int -2147483648;
+ return (n, p);
+}
+
+hex(a: array of byte): int
+{
+ n := 0;
+ for(i := 0; i < len a; i++){
+ c := int a[i];
+ if(c >= '0' && c <= '9')
+ c -= '0';
+ else
+ c -= 'a' - 10;
+ n = (n << 4) + (c & 15);
+ }
+ return n;
+}
+
+partition(n : int, max : int) : (int, int, int)
+{
+ p := n/max;
+ if (n%max != 0)
+ p++;
+ if (p > max)
+ p = max;
+ q := n/p;
+ r := n-p*q;
+ return (p, q, r);
+}
+
+bounds(s : string) : (int, int)
+{
+ lb := int s;
+ for (i := 0; i < len s; i++)
+ if (s[i] == '.')
+ break;
+ if (i+1 >= len s || s[i] != '.' || s[i+1] != '.')
+ return (1, 0);
+ ub := int s[i+2:];
+ return (lb, ub);
+}