xref: /inferno-os/appl/lib/debug.b (revision 7ef44d652ae9e5e1f5b3465d73684e4a54de73c0)
1implement Debug;
2
3include "sys.m";
4sys: Sys;
5sprint, FD: import sys;
6
7include "string.m";
8str: String;
9
10include "draw.m";
11
12include "debug.m";
13
14include "dis.m";
15	dism: Dis;
16
17Command: module
18{
19	init:	fn(ctxt: ref Draw->Context, argv: list of string);
20};
21
22Spin: adt
23{
24	spin:	int;
25	pspin:	int;
26};
27
28SrcState: adt
29{
30	files:	array of string;
31	lastf:	int;
32	lastl:	int;
33	vers:	int;			# version number
34					# 11 => more source states
35};
36
37typenames := array[] of {
38	Terror => "error",
39	Tid => "id",
40	Tadt => "adt",
41	Tadtpick => "adtpick",
42	Tarray => "array",
43	Tbig => "big",
44	Tbyte => "byte",
45	Tchan => "chan",
46	Treal => "real",
47	Tfn => "fn",
48	Targ => "arg",
49	Tlocal => "local",
50	Tglobal => "global",
51	Tint => "int",
52	Tlist => "list",
53	Tmodule => "module",
54	Tnil => "nil",
55	Tnone => "none",
56	Tref => "ref",
57	Tstring => "string",
58	Ttuple => "tuple",
59	Tend => "end",
60	Targs => "args",
61	Tslice => "slice",
62	Tpoly => "poly",
63};
64
65tnone:		ref Type;
66tnil:		ref Type;
67tint:		ref Type;
68tbyte:		ref Type;
69tbig:		ref Type;
70treal:		ref Type;
71tstring:	ref Type;
72tpoly:	ref Type;
73
74IBY2WD:		con 4;
75IBY2LG:		con 8;
76H:		con int 16rffffffff;
77
78ModHash:	con 32;
79SymHash:	con 32;
80mods:=		array[ModHash] of list of ref Module;
81syms:=		array[SymHash] of list of ref Sym;
82
83sblpath :=	array[] of
84{
85	("/dis/",	"/appl/cmd/"),
86	("/dis/",	"/appl/"),
87};
88
89init(): int
90{
91	sys = load Sys Sys->PATH;
92	str = load String String->PATH;
93	if(sys == nil || str == nil)
94		return 0;
95	tnone = ref Type(nil, Tnone, 0, "", nil, nil, nil);
96	tnil = ref Type(nil, Tnil, IBY2WD, "nil", nil, nil, nil);
97	tint = ref Type(nil, Tint, IBY2WD, "int", nil, nil, nil);
98	tbyte = ref Type(nil, Tbyte, 1, "byte", nil, nil, nil);
99	tbig = ref Type(nil, Tbig, IBY2LG, "big", nil, nil, nil);
100	treal = ref Type(nil, Treal, IBY2LG, "real", nil, nil, nil);
101	tstring = ref Type(nil, Tstring, IBY2WD, "string", nil, nil, nil);
102	tpoly = ref Type(nil, Tpoly, IBY2WD, "polymorphic", nil, nil, nil);
103	return 1;
104}
105
106prog(pid: int): (ref Prog, string)
107{
108	spid := string pid;
109	h := sys->open("/prog/"+spid+"/heap", sys->ORDWR);
110	if(h == nil)
111		return (nil, sprint("can't open heap file: %r"));
112	c := sys->open("/prog/"+spid+"/ctl", sys->OWRITE);
113	if(c == nil)
114		return (nil, sprint("can't open ctl file: %r"));
115	d := sys->open("/prog/"+spid+"/dbgctl", sys->ORDWR);
116	if(d == nil)
117		return (nil, sprint("can't open debug ctl file: %r"));
118	s := sys->open("/prog/"+spid+"/stack", sys->OREAD);
119	if(s == nil)
120		return (nil, sprint("can't open stack file: %r"));
121	return (ref Prog(pid, h, c, d, s), "");
122}
123
124startprog(dis, dir: string, ctxt: ref Draw->Context, argv: list of string): (ref Prog, string)
125{
126	c := load Command dis;
127	if(c == nil)
128		return (nil, "module not loaded");
129
130	ack := chan of int;
131	spin := ref Spin(1, 1);
132	end := chan of int;
133	spawn execer(ack, dir, c, ctxt, argv, spin, end);
134	kid := <-ack;
135
136	fd := sys->open("/prog/"+string kid+"/dbgctl", sys->ORDWR);
137	if(fd == nil){
138		spin.spin = -1;
139		<- end;
140		return (nil, sprint("can't open debug ctl file: %r"));
141	}
142	done := chan of string;
143	spawn stepper(done, fd, spin);
144
145wait:	for(;;){
146		alt{
147		<-ack =>
148			sys->sleep(0);
149		err := <-done =>
150			if(err != ""){
151				<- end;
152				return(nil, err);
153			}
154			break wait;
155		}
156	}
157
158	b := array[20] of byte;
159	n := sys->read(fd, b, len b);
160	if(n <= 0){
161		<- end;
162		return(nil, sprint("%r"));
163	}
164	msg := string b[:n];
165	if(!str->prefix("new ", msg)){
166		<- end;
167		return (nil, msg);
168	}
169
170	kid = int msg[len "new ":];
171
172	# clean up the execer slave
173	b = array of byte "start";
174	sys->write(fd, b, len b);
175
176	<- end;
177	return prog(kid);
178}
179
180stepper(done: chan of string, ctl: ref FD, spin: ref Spin)
181{
182	b := array of byte "step1";
183	while(spin.pspin){
184		if(sys->write(ctl, b, len b) != len b)
185			done <-= sprint("can't start new thread: %r");
186		spin.spin = 0;
187	}
188	done <-= "";
189}
190
191execer(ack: chan of int, dir: string, c: Command, ctxt: ref Draw->Context, args: list of string, spin: ref Spin, end: chan of int)
192{
193	pid := sys->pctl(Sys->NEWPGRP|Sys->FORKNS|Sys->NEWFD, 0::1::2::nil);
194	sys->chdir(dir);
195	while(spin.spin == 1)
196		ack <-= pid;
197	if(spin.spin == -1){
198		end <-= 0;
199		exit;
200	}
201	spawn c->init(ctxt, args);
202	spin.pspin = 0;
203	end <-= 0;
204	exit;
205}
206
207# format of each line is
208# fp pc mp prog compiled path
209# fp, pc, mp, and prog are %.8lux
210# compile is  or 1
211# path is a string
212Prog.stack(p: self ref Prog): (array of ref Exp, string)
213{
214	buf := array[8192] of byte;
215	sys->seek(p.stk, big 0, 0);
216	n := sys->read(p.stk, buf, len buf - 1);
217	if(n < 0)
218		return (nil, sprint("can't read stack file: %r"));
219	buf[n] = byte 0;
220
221	t := 0;
222	nf := 0;
223	for(s := 0; s < n; s = t+1){
224		t = strchr(buf, s, '\n');
225		if(buf[t] != byte '\n' || t-s < 40)
226			continue;
227		nf++;
228	}
229
230	e := array[nf] of ref Exp;
231	nf = 0;
232	for(s = 0; s < n; s = t+1){
233		t = strchr(buf, s, '\n');
234		if(buf[t] != byte '\n' || t-s < 40)
235			continue;
236		e[nf] = ref Exp("unknown fn",
237				hex(buf[s+0:s+8]),
238				hex(buf[s+9:s+17]),
239				mkmod(hex(buf[s+18:s+26]), hex(buf[s+27:s+35]), buf[36] != byte '0', string buf[s+38:t]),
240				p,
241				nil);
242		nf++;
243	}
244
245	return (e, "");
246}
247
248Prog.step(p: self ref Prog, how: int): string
249{
250	(stack, nil) := p.stack();
251	if(stack == nil)
252		return "can't find initial pc";
253	src := stack[0].srcstr();
254	stmt := ftostmt(stack[0]);
255
256	if(stack[0].m.sym == nil)
257		how = -1;
258
259	buf := array of byte("step1");
260	if(how == StepOut)
261		buf = array of byte("toret");
262	while(sys->write(p.dbgctl, buf, len buf) == len buf){
263		(stk, err) := p.stack();
264		if(err != nil)
265			return "";
266		case how{
267		StepExp =>
268			if(src != stk[0].srcstr())
269				return "";
270		StepStmt =>
271			if(stmt != ftostmt(stk[0]))
272				return "";
273			if(stk[0].offset != stack[0].offset)
274				return "";
275		StepOut =>
276			if(returned(stack, stk))
277				return "";
278		StepOver =>
279			if(stk[0].offset == stack[0].offset){
280				if(stmt != ftostmt(stk[0]))
281					return "";
282				buf = array of byte("step1");
283				break;
284			}
285			if(returned(stack, stk))
286				return "";
287			buf = array of byte("toret");
288		* =>
289			return "";
290		}
291	}
292	return sprint("%r");
293}
294
295Prog.stop(p: self ref Prog): string
296{
297	return dbgctl(p, "stop");
298}
299
300Prog.unstop(p: self ref Prog): string
301{
302	return dbgctl(p, "unstop");
303}
304
305Prog.grab(p: self ref Prog): string
306{
307	return dbgctl(p, "step0");
308}
309
310Prog.start(p: self ref Prog): string
311{
312	return dbgctl(p, "start");
313}
314
315Prog.cont(p: self ref Prog): string
316{
317	return dbgctl(p, "cont");
318}
319
320dbgctl(p: ref Prog, msg: string): string
321{
322	b := array of byte msg;
323	while(sys->write(p.dbgctl, b, len b) != len b)
324		return sprint("%r");
325	return "";
326}
327
328returned(old, new: array of ref Exp): int
329{
330	n := len old;
331	if(n > len new)
332		return 1;
333	return 0;
334}
335
336Prog.setbpt(p: self ref Prog, dis: string, pc:int): string
337{
338	b := array of byte("bpt set "+dis+" "+string pc);
339	if(sys->write(p.dbgctl, b, len b) != len b)
340		return sprint("can't set breakpoint: %r");
341	return "";
342}
343
344Prog.delbpt(p: self ref Prog, dis: string, pc:int): string
345{
346	b := array of byte("bpt del "+dis+" "+string pc);
347	if(sys->write(p.dbgctl, b, len b) != len b)
348		return sprint("can't del breakpoint: %r");
349	return "";
350}
351
352Prog.kill(p: self ref Prog): string
353{
354	b := array of byte "kill";
355	if(sys->write(p.ctl, b, len b) != len b)
356		return sprint("can't kill process: %r");
357	return "";
358}
359
360Prog.event(p: self ref Prog): string
361{
362	b := array[100] of byte;
363	n := sys->read(p.dbgctl, b, len b);
364	if(n < 0)
365		return sprint("error: %r");
366	return string b[:n];
367}
368
369ftostmt(e: ref Exp): int
370{
371	m := e.m;
372	if(!m.comp && m.sym != nil && e.pc < len m.sym.srcstmt)
373		return m.sym.srcstmt[e.pc];
374	return -1;
375}
376
377Exp.srcstr(e: self ref Exp): string
378{
379	m := e.m;
380	if(!m.comp && m.sym != nil && e.pc < len m.sym.src){
381		src := m.sym.src[e.pc];
382		ss := src.start.file+":"+string src.start.line+"."+string src.start.pos+", ";
383		if(src.stop.file != src.start.file)
384			ss += src.stop.file+":"+string src.stop.line+".";
385		else if(src.stop.line != src.start.line)
386			ss += string src.stop.line+".";
387		return ss+string src.stop.pos;
388	}
389	return sprint("Module %s PC %d", e.m.path, e.pc);
390}
391
392Exp.findsym(e: self ref Exp): string
393{
394	m := e.m;
395	if(m.comp)
396		return "compiled module";
397	if(m.sym != nil){
398		n := e.pc;
399		fns := m.sym.fns;
400		for(i := 0; i < len fns; i++){
401			if(n >= fns[i].offset && n < fns[i].stoppc){
402				e.name = fns[i].name;
403				e.id = fns[i];
404				return "";
405			}
406		}
407		return "pc out of bounds";
408	}
409	return "no symbol file";
410}
411
412Exp.src(e: self ref Exp): ref Src
413{
414	m := e.m;
415	if(e.id == nil || m.sym == nil)
416		return nil;
417	src := e.id.src;
418	if(src != nil)
419		return src;
420	if(e.id.t.kind == Tfn && !m.comp && e.pc < len m.sym.src && e.pc >= 0)
421		return m.sym.src[e.pc];
422	return nil;
423}
424
425Type.getkind(t: self ref Type, sym: ref Sym): int
426{
427	if(t == nil)
428		return -1;
429	if(t.kind == Tid)
430		return sym.adts[int t.name].getkind(sym);
431	return t.kind;
432}
433
434Type.text(t: self ref Type, sym: ref Sym): string
435{
436	if (t == nil)
437		return "no type";
438	s := typenames[t.kind];
439	case t.kind {
440	Tadt or
441	Tadtpick or
442	Tmodule =>
443		s = t.name;
444	Tid =>
445		return sym.adts[int t.name].text(sym);
446	Tarray or
447	Tlist or
448	Tchan or
449	Tslice =>
450		s += " of " + t.Of.text(sym);
451	Tref =>
452		s += " " + t.Of.text(sym);
453	Tfn =>
454		s += "(";
455		for(i := 0; i < len t.ids; i++)
456			s += t.ids[i].name + ": " + t.ids[i].t.text(sym);
457		s += "): " + t.Of.text(sym);
458	Ttuple or
459	Tlocal or
460	Tglobal or
461	Targ =>
462		if(t.kind == Ttuple)
463			s = "";
464		s += "(";
465		for (i := 0; i < len t.ids; i++) {
466			s += t.ids[i].t.text(sym);
467			if (i < len t.ids - 1)
468				s += ", ";
469		}
470		s += ")";
471	}
472	return s;
473}
474
475Exp.typename(e: self ref Exp): string
476{
477	if (e.id == nil)
478		return "no info";
479	return e.id.t.text(e.m.sym);
480}
481
482Exp.kind(e: self ref Exp): int
483{
484	if(e.id == nil)
485		return -1;
486	return e.id.t.getkind(e.m.sym);
487}
488
489EXPLISTMAX : con	32;	# what's a good value for this ?
490
491Exp.expand(e: self ref Exp): array of ref Exp
492{
493	if(e.id == nil)
494		return nil;
495
496	t := e.id.t;
497	if(t.kind == Tid)
498		t = e.m.sym.adts[int t.name];
499
500	off := e.offset;
501	ids := t.ids;
502	case t.kind{
503	Tadt or Tfn or Targ or Tlocal or Ttuple =>
504		break;
505	Tadtpick =>
506		break;
507	Tglobal =>
508		ids = e.m.sym.vars;
509		off = e.m.data;
510	Tmodule =>
511		(s, err) := pdata(e.p, off, "M");
512		if(s == "nil" || err != "")
513			return nil;
514		off = hex(array of byte s);
515	Tref =>
516		(s, err) := pdata(e.p, off, "P");
517		if(s == "nil" || err != "")
518			return nil;
519		off = hex(array of byte s);
520		et := t.Of;
521		if(et.kind == Tid)
522			et = e.m.sym.adts[int et.name];
523		ids = et.ids;
524		if(et.kind == Tadtpick){
525			(s, err) = pdata(e.p, off, "W");
526			tg := int s;
527			if(tg < 0 || tg > len et.tags || err != "" )
528				return nil;
529			k := array[1 + len ids + len et.tags[tg].ids] of ref Exp;
530			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));
531			x := 1;
532			for(i := 0; i < len ids; i++){
533				id := ids[i];
534				k[i+x] = ref Exp(id.name, off+id.offset, e.pc, e.m, e.p, id);
535			}
536			x += len ids;
537			ids = et.tags[tg].ids;
538			for(i = 0; i < len ids; i++){
539				id := ids[i];
540				k[i+x] = ref Exp(id.name, off+id.offset, e.pc, e.m, e.p, id);
541			}
542			return k;
543		}
544	Tlist =>
545		(s, err) := pdata(e.p, off, "L");
546		if(err != "")
547			return nil;
548		(tloff, hdoff) := str->splitl(s, ".");
549		hdoff = hdoff[1:];
550		k := array[2] of ref Exp;
551		k[0] = ref Exp("hd", hex(array of byte hdoff), e.pc, e.m, e.p, ref Id(nil, "hd", H, H, t.Of));
552		k[1] = ref Exp("tl", hex(array of byte tloff), e.pc, e.m, e.p, ref Id(nil, "tl", H, H, t));
553		return k;
554	Tarray =>
555		(s, nil) := pdata(e.p, e.offset, "A");
556		if(s == "nil")
557			return nil;
558		(sn, sa) := str->splitl(s, ".");
559		n := int sn;
560		if(sa == "" || n <= 0)
561			return nil;
562		(off, nil) = str->toint(sa[1:], 16);
563		et := t.Of;
564		if(et.kind == Tid)
565			et = e.m.sym.adts[int et.name];
566		esize := et.size;
567		if (n <= EXPLISTMAX || EXPLISTMAX == 0) {
568			k := array[n] of ref Exp;
569			for(i := 0; i < n; i++){
570				name := string i;
571				k[i] = ref Exp(name, off+i*esize, e.pc, e.m, e.p, ref Id(nil, name, H, H, et));
572			}
573			return k;
574		}
575		else {
576			# slice it
577			(p, q, r) := partition(n, EXPLISTMAX);
578			lb := 0;
579			k := array[p] of ref Exp;
580			st := ref Type(et.src, Tslice, 0, nil, et, nil, nil);
581			for (i := 0; i < p; i++){
582				ub := lb+q-1;
583				if (--r >= 0)
584					ub++;
585				name := string lb + ".." + string ub;
586				k[i] = ref Exp(name, off+lb*esize, e.pc, e.m, e.p, ref Id(nil, name, H, H, st));
587				lb = ub+1;
588			}
589			return k;
590		}
591	Tslice =>
592		(lb, ub) := bounds(e.name);
593		if (lb > ub)
594			return nil;
595		n := ub-lb+1;
596		et := t.Of;
597		if(et.kind == Tid)
598			et = e.m.sym.adts[int et.name];
599		esize := et.size;
600		if (n <= EXPLISTMAX || EXPLISTMAX == 0) {
601			k := array[n] of ref Exp;
602			for(i := 0; i < n; i++){
603				name := string (i+lb);
604				k[i] = ref Exp(name, off+i*esize, e.pc, e.m, e.p, ref Id(nil, name, H, H, et));
605			}
606			return k;
607		}
608		else {
609			# slice it again
610			(p, q, r) := partition(n, EXPLISTMAX);
611			lb0 := lb;
612			k := array[p] of ref Exp;
613			st := ref Type(et.src, Tslice, 0, nil, et, nil, nil);
614			for (i := 0; i < p; i++){
615				ub = lb+q-1;
616				if (--r >= 0)
617					ub++;
618				name := string lb + ".." + string ub;
619				k[i] = ref Exp(name, off+(lb-lb0)*esize, e.pc, e.m, e.p, ref Id(nil, name, H, H, st));
620				lb = ub+1;
621			}
622			return k;
623		}
624	Tchan =>
625		(s, nil) := pdata(e.p, e.offset, "c");
626		if(s == "nil")
627			return nil;
628		(sn, sa) := str->splitl(s, ".");
629		n := int sn;
630		if(sa == "" || n <= 0)
631			return nil;
632		(off, nil) = str->toint(sa[1:], 16);
633		(nil, sa) = str->splitl(sa[1:], ".");
634		(sn, sa) = str->splitl(sa[1:], ".");
635		f := int sn;
636		sz := int sa[1:];
637		et := t.Of;
638		if(et.kind == Tid)
639			et = e.m.sym.adts[int et.name];
640		esize := et.size;
641		k := array[sz] of ref Exp;
642		for(i := 0; i < sz; i++){
643			name := string i;
644			j := (f+i)%n;
645			k[i] = ref Exp(name, off+j*esize, e.pc, e.m, e.p, ref Id(nil, name, H, H, et));
646		}
647		return k;
648	* =>
649		return nil;
650	}
651	k := array[len ids] of ref Exp;
652	for(i := 0; i < len k; i++){
653		id := ids[i];
654		k[i] = ref Exp(id.name, off+id.offset, e.pc, e.m, e.p, id);
655	}
656	return k;
657}
658
659Exp.val(e: self ref Exp): (string, int)
660{
661	if(e.id == nil)
662		return (e.m.path+" unknown fn", 0);
663	t := e.id.t;
664	if(t.kind == Tid)
665		t = e.m.sym.adts[int t.name];
666
667	w := 0;
668	s := "";
669	err := "";
670	p := e.p;
671	case t.kind{
672	Tfn =>
673		if(t.ids != nil)
674			w = 1;
675		src := e.m.sym.src[e.pc];
676		ss := src.start.file+":"+string src.start.line+"."+string src.start.pos+", ";
677		if(src.stop.file != src.start.file)
678			ss += src.stop.file+":"+string src.stop.line+".";
679		else if(src.stop.line != src.start.line)
680			ss += string src.stop.line+".";
681		return (ss+string src.stop.pos, w);
682	Targ or Tlocal or Tglobal or Tadtpick or Ttuple =>
683		return ("", 1);
684	Tadt =>
685		return ("#" + string e.offset, 1);
686	Tnil =>
687		s = "nil";
688	Tbyte =>
689		(s, err) = pdata(p, e.offset, "B");
690	Tint =>
691		(s, err) = pdata(p, e.offset, "W");
692	Tbig =>
693		(s, err) = pdata(p, e.offset, "V");
694	Treal =>
695		(s, err) = pdata(p, e.offset, "R");
696	Tarray =>
697		(s, err) = pdata(p, e.offset, "A");
698		if(s == "nil")
699			break;
700		(n, a) := str->splitl(s, ".");
701		if(a == "")
702			return ("", 0);
703		s = "["+n+"] @"+a[1:];
704		w = 1;
705	Tslice =>
706		(lb, ub) := bounds(e.name);
707		s = sys->sprint("[:%d] @ %x", ub-lb+1, e.offset);
708		w = 1;
709	Tstring =>
710		n : int;
711		(n, s, err) = pstring(p, e.offset);
712		if(err != "")
713			return ("", 0);
714		for(i := 0; i < len s; i++)
715			if(s[i] == '\n')
716				s[i] = '\u008a';
717		s = "["+string n+"] \""+s+"\"";
718	Tref or Tlist or Tmodule or Tpoly=>
719		(s, err) = pdata(p, e.offset, "P");
720		if(s == "nil")
721			break;
722		s = "@" + s;
723		w = 1;
724	Tchan =>
725		(s, err) = pdata(p, e.offset, "c");
726		if(s == "nil")
727			break;
728		(n, a) := str->splitl(s, ".");
729		if(a == "")
730			return ("", 0);
731		if(n == "0"){
732			s = "@" + a[1:];
733			w = 0;
734		}
735		else{
736			(a, nil) = str->splitl(a[1:], ".");
737			s = "["+n+"] @"+a;
738			w = 1;
739		}
740	}
741	if(err != "")
742		return ("", 0);
743	return (s, w);
744}
745
746Sym.srctopc(s: self ref Sym, src: ref Src): int
747{
748	srcs := s.src;
749	line := src.start.line;
750	pos := src.start.pos;
751	(nil, file) := str->splitr(src.start.file, "/");
752	backup := -1;
753	delta := 80;
754	for(i := 0; i < len srcs; i++){
755		ss := srcs[i];
756		if(ss.start.file != file)
757			continue;
758		if(ss.start.line <= line && ss.start.pos <= pos
759		&& ss.stop.line >= line && ss.stop.pos >= pos)
760			return i;
761		d := ss.start.line - line;
762		if(d >= 0 && d < delta){
763			delta = d;
764			backup = i;
765		}
766	}
767	return backup;
768}
769
770Sym.pctosrc(s: self ref Sym, pc: int): ref Src
771{
772	if(pc < 0 || pc >= len s.src)
773		return nil;
774	return s.src[pc];
775}
776
777sym(sbl: string): (ref Sym, string)
778{
779	h := 0;
780	for(i := 0; i < len sbl; i++)
781		h = (h << 1) + sbl[i];
782	h &= SymHash - 1;
783	for(sl := syms[h]; sl != nil; sl = tl sl){
784		s := hd sl;
785		if(sbl == s.path)
786			return (s, "");
787	}
788	(sy, err) := loadsyms(sbl);
789	if(err != "")
790		return (nil, err);
791	syms[h] = sy :: syms[h];
792	return (sy, "");
793}
794
795Module.addsym(m: self ref Module, sym: ref Sym)
796{
797	m.sym = sym;
798}
799
800Module.sbl(m: self ref Module): string
801{
802	if(m.sym != nil)
803		return m.sym.path;
804	return "";
805}
806
807Module.dis(m: self ref Module): string
808{
809	return m.path;
810}
811
812findsbl(dis: string): string
813{
814	n  := len dis;
815	if(n <= 4 || dis[n-4: n] != ".dis")
816		dis += ".dis";
817	if(dism == nil){
818		dism = load Dis Dis->PATH;
819		if(dism != nil)
820			dism->init();
821	}
822	if(dism != nil && (b := dism->src(dis)) != nil){
823		n = len b;
824		if(n > 2 && b[n-2: n] == ".b"){
825			sbl := b[0: n-2] + ".sbl";
826			if(sys->open(sbl, Sys->OREAD) != nil)
827				return sbl;
828		}
829	}
830	return nil;
831}
832
833Module.stdsym(m: self ref Module)
834{
835	if(m.sym != nil)
836		return;
837	if((sbl := findsbl(m.path)) != nil){
838		(m.sym, nil) = sym(sbl);
839		return;
840	}
841	sbl = m.path;
842	n := len sbl;
843	if(n > 4 && sbl[n-4:n] == ".dis")
844		sbl = sbl[:n-4]+".sbl";
845	else
846		sbl = sbl+".sbl";
847	path := sbl;
848	fd := sys->open(sbl, sys->OREAD);
849	for(i := 0; fd == nil && i < len sblpath; i++){
850		(dis, src) := sblpath[i];
851		nd := len dis;
852		if(len sbl > nd && sbl[:nd] == dis){
853			path = src + sbl[nd:];
854			fd = sys->open(path, sys->OREAD);
855		}
856	}
857	if(fd == nil)
858		return;
859	(m.sym, nil) = sym(path);
860}
861
862mkmod(data, code, comp: int, dis: string): ref Module
863{
864	h := 0;
865	for(i := 0; i < len dis; i++)
866		h = (h << 1) + dis[i];
867	h &= ModHash - 1;
868	sym : ref Sym;
869	for(ml := mods[h]; ml != nil; ml = tl ml){
870		m := hd ml;
871		if(m.path == dis && m.code == code && m.comp == comp){
872			sym = m.sym;
873			if(m.data == data)
874				return m;
875		}
876	}
877	m := ref Module(dis, code, data, comp, sym);
878	mods[h] = m :: mods[h];
879	return m;
880}
881
882pdata(p: ref Prog, a: int, fmt: string): (string, string)
883{
884	b := array of byte sprint("0x%ux.%s1", a, fmt);
885	if(sys->write(p.heap, b, len b) != len b)
886		return ("", sprint("can't write heap: %r"));
887
888	buf := array[64] of byte;
889	sys->seek(p.heap, big 0, 0);
890	n := sys->read(p.heap, buf, len buf);
891	if(n <= 1)
892		return ("", sprint("can't read heap: %r"));
893	return (string buf[:n-1], "");
894}
895
896pstring0(p: ref Prog, a: int, blen: int): (int, string, string)
897{
898	b := array of byte sprint("0x%ux.C1", a);
899	if(sys->write(p.heap, b, len b) != len b)
900		return (-1, "", sprint("can't write heap: %r"));
901
902	buf := array[blen] of byte;
903	sys->seek(p.heap, big 0, 0);
904	n := sys->read(p.heap, buf, len buf-1);
905	if(n <= 1)
906		return (-1, "", sprint("can't read heap: %r"));
907	buf[n] = byte 0;
908	m := strchr(buf, 0, '.');
909	if(buf[m++] != byte '.')
910		m = 0;
911	return (int string buf[0:m], string buf[m:n], "");
912}
913
914pstring(p: ref Prog, a: int): (int, string, string)
915{
916	m, n: int;
917	s, err: string;
918
919	m = 64;
920	for(;;){
921		(n, s, err) = pstring0(p, a, m);
922		if(err != "" || n <= len s)
923			break;
924		# guard against broken devprog
925		if(m >= 3 * n)
926			return (-1, nil, "bad string");
927		m *= 2;
928	}
929	return (n, s, err);
930}
931
932Prog.status(p: self ref Prog): (int, string, string, string)
933{
934	fd := sys->open(sprint("/prog/%d/status", p.id), sys->OREAD);
935	if(fd == nil)
936		return (-1, "", sprint("can't open status file: %r"), "");
937	buf := array[256] of byte;
938	n := sys->read(fd, buf, len buf);
939	if(n <= 0)
940		return (-1, "", sprint("can't read status file: %r"), "");
941	(ni, info) := sys->tokenize(string buf[:n], " \t");
942	if(ni != 6 && ni != 7)
943		return (-1, "", "can't parse status file", "");
944	info = tl info;
945	if(ni == 6)
946		return (int hd info, hd tl info, hd tl tl info, hd tl tl tl tl info);
947	return (int hd info, hd tl info, hd tl tl tl info, hd tl tl tl tl tl info);
948}
949
950loadsyms(sbl: string): (ref Sym, string)
951{
952	fd := sys->open(sbl, sys->OREAD);
953	if(fd == nil)
954		return (nil, sprint("Can't open symbol file '%s': %r", sbl));
955
956	(ok, dir) := sys->fstat(fd);
957	if(ok < 0)
958		return (nil, sprint("Can't read symbol file '%s': %r", sbl));
959	n := int dir.length;
960	buf := array[n+1] of byte;
961	if(sys->read(fd, buf, n) != n)
962		return (nil, sprint("Can't read symbol file '%s': %r", sbl));
963	fd = nil;
964	buf[n] = byte 0;
965
966	s := ref Sym;
967	s.path = sbl;
968
969	n = strchr(buf, 0, '\n');
970	vers := 0;
971	if(string buf[:n] == "limbo .sbl 1.")
972		vers = 10;
973	else if(string buf[:n] == "limbo .sbl 1.1")
974		vers = 11;
975	else if(string buf[:n] == "limbo .sbl 2.0")
976		vers = 20;
977	else if(string buf[:n] == "limbo .sbl 2.1")
978		vers = 21;
979	else
980		return (nil, "Symbol file "+sbl+" out of date");
981	o := n += 1;
982	n = strchr(buf, o, '\n');
983	if(buf[n] != byte '\n')
984		return (nil, "Corrupted symbol file "+sbl);
985	s.name = string buf[o:n++];
986	ss := ref SrcState(nil, 0, 0, vers);
987	err : string;
988	if(n >= 0){
989		err = "file";
990		n = debugfiles(ss, buf, n);
991	}
992	if(n >= 0){
993		err = "pc";
994		n = debugpc(ss, s, buf, n);
995	}
996	if(n >= 0){
997		err = "types";
998		n = debugtys(ss, s, buf, n);
999	}
1000	if(n >= 0){
1001		err = "fn";
1002		n = debugfns(ss, s, buf, n);
1003	}
1004	vs: array of ref Id;
1005	if(n >= 0){
1006		err = "global";
1007		(vs, n) = debugid(ss, buf, n);
1008	}
1009	if(n < 0)
1010		return (nil, "Corrupted "+err+" symbol table in "+sbl);
1011	s.vars = vs;
1012	return (s, "");
1013}
1014
1015#
1016# parse a source location
1017# format[file:][line.]pos,[file:][line.]pos' '
1018#
1019debugsrc(ss: ref SrcState, buf: array of byte, p: int): (ref Src, int)
1020{
1021	n: int;
1022	src: ref Src;
1023
1024	(n, p) = strtoi(buf, p);
1025	if(buf[p] == byte ':'){
1026		ss.lastf = n;
1027		(n, p) = strtoi(buf, p + 1);
1028	}
1029	if(buf[p] == byte '.'){
1030		ss.lastl = n;
1031		(n, p) = strtoi(buf, p + 1);
1032	}
1033	if(buf[p++] != byte ',' || ss.lastf >= len ss.files || ss.lastf < 0)
1034		return (nil, -1);
1035	src = ref Src;
1036	src.start.file = ss.files[ss.lastf];
1037	src.start.line = ss.lastl;
1038	src.start.pos = n;
1039
1040	(n, p) = strtoi(buf, p);
1041	if(buf[p] == byte ':'){
1042		ss.lastf = n;
1043		(n, p) = strtoi(buf, p+1);
1044	}
1045	if(buf[p] == byte '.'){
1046		ss.lastl = n;
1047		(n, p) = strtoi(buf, p + 1);
1048	}
1049	if(buf[p++] != byte ' ' || ss.lastf >= len ss.files || ss.lastf < 0)
1050		return (nil, -1);
1051	src.stop.file = ss.files[ss.lastf];
1052	src.stop.line = ss.lastl;
1053	src.stop.pos = n;
1054	return (src, p);
1055}
1056
1057#
1058# parse the file table
1059# item format: file: string
1060#
1061debugfiles(ss: ref SrcState, buf: array of byte, p: int): int
1062{
1063	n, q: int;
1064
1065	(n, p) = strtoi(buf, p);
1066	if(buf[p++] != byte '\n')
1067		return -1;
1068	ss.files = array[n] of string;
1069	for(i := 0; i < n; i++){
1070		q = strchr(buf, p, '\n');
1071		ss.files[i] = string buf[p:q];
1072		p = q + 1;
1073	}
1074	return p;
1075}
1076
1077#
1078# parse the pc to source table
1079# item format: Source stmt
1080#
1081debugpc(ss: ref SrcState, s: ref Sym, buf: array of byte, p: int): int
1082{
1083	ns: int;
1084
1085	(ns, p) = strtoi(buf, p);
1086	if(buf[p++] != byte '\n')
1087		return -1;
1088	s.src = array[ns] of ref Src;
1089	s.srcstmt = array[ns] of int;
1090	for(i := 0; i < ns; i++){
1091		(s.src[i], p) = debugsrc(ss, buf, p);
1092		if(p < 0)
1093			return -1;
1094		(s.srcstmt[i], p) = strtoi(buf, p);
1095		if(buf[p++] != byte '\n')
1096			return -1;
1097	}
1098	return p;
1099}
1100
1101#
1102# parse the type table
1103# format: linear list of types
1104#
1105debugtys(ss: ref SrcState, s: ref Sym, buf: array of byte, p: int): int
1106{
1107	na: int;
1108
1109	(na, p) = strtoi(buf, p);
1110	if(buf[p++] != byte '\n')
1111		return -1;
1112	s.adts = array[na] of ref Type;
1113	adts := s.adts;
1114	for(i := 0; i < na; i++){
1115		if(ss.vers < 20)
1116			(adts[i], p) = debugadt(ss, buf, p);
1117		else
1118			(adts[i], p) = debugtype(ss, buf, p);
1119		if(p < 0)
1120			return -1;
1121	}
1122	return p;
1123}
1124
1125#
1126# parse the function table
1127# format: pc:name:argids localids rettype
1128#
1129debugfns(ss: ref SrcState, s: ref Sym, buf: array of byte, p: int): int
1130{
1131	t: ref Type;
1132	args, locals: array of ref Id;
1133	nf, pc, q: int;
1134
1135	(nf, p) = strtoi(buf, p);
1136	if(buf[p++] != byte '\n')
1137		return -1;
1138	s.fns = array[nf] of ref Id;
1139	fns := s.fns;
1140	for(i := 0; i < nf; i++){
1141		(pc, p) = strtoi(buf, p);
1142		if(buf[p++] != byte ':')
1143			return -2;
1144		q = strchr(buf, p, '\n');
1145		if(buf[q] != byte '\n')
1146			return -3;
1147		name := string buf[p:q];
1148		(args, p) = debugid(ss, buf, q + 1);
1149		if(p == -1)
1150			return -4;
1151		(locals, p) = debugid(ss, buf, p);
1152		if(p == -1)
1153			return -5;
1154		(t, p) = debugtype(ss, buf, p);
1155		if(p == -1)
1156			return -6;
1157		nk := 1 + (len args != 0) + (len locals != 0);
1158		kids := array[nk] of ref Id;
1159		nk = 0;
1160		if(len locals != 0)
1161			kids[nk++] = ref Id(nil, "locals", 0, 0, ref Type(nil, Tlocal, 0, nil, nil, locals, nil));
1162		if(len args != 0)
1163			kids[nk++] = ref Id(nil, "args", 0, 0, ref Type(nil, Targ, 0, nil, nil, args, nil));
1164		kids[nk++] = ref Id(nil, "module", 0, 0, ref Type(nil, Tglobal, 0, nil, nil, nil, nil));
1165		args = nil;
1166		locals = nil;
1167		fns[i] = ref Id(nil, name, pc, 0, ref Type(nil, Tfn, 0, name, t, kids, nil));
1168	}
1169	for(i = 1; i < nf; i++)
1170		fns[i-1].stoppc = fns[i].offset;
1171	fns[i-1].stoppc = len s.src;
1172	return p;
1173}
1174
1175#
1176# parse a list of ids
1177# format: offset ':' name ':' src type '\n'
1178#
1179debugid(ss: ref SrcState, buf: array of byte, p: int): (array of ref Id, int)
1180{
1181	t: ref Type;
1182	off, nd, q, qq, tq: int;
1183	src: ref Src;
1184
1185	(nd, p) = strtoi(buf, p);
1186	if(buf[p++] != byte '\n')
1187		return (nil, -1);
1188	d := array[nd] of ref Id;
1189	for(i := 0; i < nd; i++){
1190		(off, q) = strtoi(buf, p);
1191		if(buf[q++] != byte ':')
1192			return (nil, -1);
1193		qq = strchr(buf, q, ':');
1194		if(buf[qq] != byte ':')
1195			return (nil, -1);
1196		tq = qq + 1;
1197		if(ss.vers > 10){
1198			(src, tq) = debugsrc(ss, buf, tq);
1199			if(tq < 0)
1200				return (nil, -1);
1201		}
1202		(t, p) = debugtype(ss, buf, tq);
1203		if(p == -1 || buf[p++] != byte '\n')
1204			return (nil, -1);
1205		d[i] = ref Id(src, string buf[q:qq], off, 0, t);
1206	}
1207	return (d, p);
1208}
1209
1210idlist(a: array of ref Id): list of ref Id
1211{
1212	n := len a;
1213	ids : list of ref Id = nil;
1214	while(n-- > 0)
1215		ids = a[n] :: ids;
1216	return ids;
1217}
1218
1219#
1220# parse a type description
1221#
1222debugtype(ss: ref SrcState, buf: array of byte, p: int): (ref Type, int)
1223{
1224	t: ref Type;
1225	d: array of ref Id;
1226	q, k: int;
1227	src: ref Src;
1228
1229	size := 0;
1230	case int buf[p++]{
1231	'@' =>
1232		k = Tid;
1233	'A' =>
1234		k = Tarray;
1235		size = IBY2WD;
1236	'B' =>
1237		return (tbig, p);
1238	'C' =>	k = Tchan;
1239		size = IBY2WD;
1240	'L' =>
1241		k = Tlist;
1242		size = IBY2WD;
1243	'N' =>
1244		return (tnil, p);
1245	'R' =>
1246		k = Tref;
1247		size = IBY2WD;
1248	'a' =>
1249		k = Tadt;
1250		if(ss.vers < 20)
1251			size = -1;
1252	'b' =>
1253		return (tbyte, p);
1254	'f' =>
1255		return (treal, p);
1256	'i' =>
1257		return (tint, p);
1258	'm' =>
1259		k = Tmodule;
1260		size = IBY2WD;
1261	'n' =>
1262		return (tnone, p);
1263	'p' =>
1264		k = Tadtpick;
1265	's' =>
1266		return (tstring, p);
1267	't' =>
1268		k = Ttuple;
1269	 	size = -1;
1270	'F' =>
1271		k = Tfn;
1272		size = IBY2WD;
1273	'P' =>
1274		return (tpoly, p);
1275	* =>
1276		k = Terror;
1277	}
1278
1279	if(size == -1){
1280		q = strchr(buf, p, '.');
1281		if(buf[q] == byte '.'){
1282			size = int string buf[p:q];
1283			p = q+1;
1284		}
1285	}
1286
1287	case k{
1288	Tid =>
1289		q = strchr(buf, p, '\n');
1290		if(buf[q] != byte '\n')
1291			return (nil, -1);
1292		t = ref Type(nil, Tid, -1, string buf[p:q], nil, nil, nil);
1293		p = q + 1;
1294	Tadt =>
1295		if(ss.vers < 20){
1296			q = strchr(buf, p, '\n');
1297			if(buf[q] != byte '\n')
1298				return (nil, -1);
1299			t = ref Type(nil, Tid, size, string buf[p:q], nil, nil, nil);
1300			p = q + 1;
1301		}else
1302			(t, p) = debugadt(ss, buf, p);
1303	Tadtpick =>
1304		(t, p) = debugadt(ss, buf, p);
1305		t.kind = Tadtpick;
1306		(t.tags, p) = debugtag(ss, buf, p);
1307	Tmodule =>
1308		q = strchr(buf, p, '\n');
1309		if(buf[q] != byte '\n')
1310			return (nil, -1);
1311		t = ref Type(nil, k, size, string buf[p:q], nil, nil, nil);
1312		p = q + 1;
1313		if(ss.vers > 10){
1314			(src, p) = debugsrc(ss, buf, p);
1315			t.src = src;
1316		}
1317		if(ss.vers > 20)
1318			(t.ids, p) = debugid(ss, buf, p);
1319	Tref or Tarray or Tlist or Tchan =>		# ref, array, list, chan
1320		(t, p) = debugtype(ss, buf, p);
1321		t = ref Type(nil, k, size, "", t, nil, nil);
1322
1323	Ttuple =>						# tuple
1324		(d, p) = debugid(ss, buf, p);
1325		t = ref Type(nil, k, size, "", nil, d, nil);
1326
1327	Tfn =>						# fn
1328		(d, p) = debugid(ss, buf, p);
1329		(t, p) = debugtype(ss, buf, p);
1330		t = ref Type(nil, k, size, "", t, d, nil);
1331
1332	* =>
1333		p = -1;
1334	}
1335	return (t, p);
1336}
1337
1338#
1339# parse an adt type spec
1340# format: name ' ' src size '\n' ids
1341#
1342debugadt(ss: ref SrcState, buf: array of byte, p: int): (ref Type, int)
1343{
1344	src: ref Src;
1345
1346	q := strchr(buf, p, ' ');
1347	if(buf[q] != byte ' ')
1348		return (nil, -1);
1349	sq := q + 1;
1350	if(ss.vers > 10){
1351		(src, sq) = debugsrc(ss, buf, sq);
1352		if(sq < 0)
1353			return (nil, -1);
1354	}
1355	qq := strchr(buf, sq, '\n');
1356	if(buf[qq] != byte '\n')
1357		return (nil, -1);
1358	(d, pp) := debugid(ss, buf, qq + 1);
1359	if(pp == -1)
1360		return (nil, -1);
1361	t := ref Type(src, Tadt, int string buf[sq:qq], string buf[p:q], nil, d, nil);
1362	return (t, pp);
1363}
1364
1365#
1366# parse a list of tags
1367# format:
1368#	name ':' src size '\n' ids
1369# or
1370#	name ':' src '\n'
1371#
1372debugtag(ss: ref SrcState, buf: array of byte, p: int): (array of ref Type, int)
1373{
1374	d: array of ref Id;
1375	ntg, q, pp, np: int;
1376	src: ref Src;
1377
1378	(ntg, p) = strtoi(buf, p);
1379	if(buf[p++] != byte '\n')
1380		return (nil, -1);
1381	tg := array[ntg] of ref Type;
1382	for(i := 0; i < ntg; i++){
1383		pp = strchr(buf, p, ':');
1384		if(buf[pp] != byte ':')
1385			return (nil, -1);
1386		q = pp + 1;
1387		(src, q) = debugsrc(ss, buf, q);
1388		if(q < 0)
1389			return (nil, -1);
1390		if(buf[q] == byte '\n'){
1391			np = q + 1;
1392			if(i <= 0)
1393				return (nil, -1);
1394			tg[i] = ref Type(src, Tadt, tg[i-1].size, string buf[p:pp], nil, tg[i-1].ids, nil);
1395		}else{
1396			np = strchr(buf, q, '\n');
1397			if(buf[np] != byte '\n')
1398				return (nil, -1);
1399			size := int string buf[q:np];
1400			(d, np) = debugid(ss, buf, np+1);
1401			if(np == -1)
1402				return (nil, -1);
1403			tg[i] = ref Type(src, Tadt, size, string buf[p:pp], nil, d, nil);
1404		}
1405		p = np;
1406	}
1407	return (tg, p);
1408}
1409
1410strchr(a: array of byte, p, c: int): int
1411{
1412	bc := byte c;
1413	while((b := a[p]) != byte 0 && b != bc)
1414		p++;
1415	return p;
1416}
1417
1418strtoi(a: array of byte, start: int): (int, int)
1419{
1420	p := start;
1421	for(; c := int a[p]; p++){
1422		case c{
1423		' ' or '\t' or '\n' or '\r' =>
1424			continue;
1425		}
1426		break;
1427	}
1428
1429	# sign
1430	neg := c == '-';
1431	if(neg || c == '+')
1432		p++;
1433
1434	# digits
1435	n := 0;
1436	nn := 0;
1437	ndig := 0;
1438	over := 0;
1439	for(; c = int a[p]; p++){
1440		if(c < '0' || c > '9')
1441			break;
1442		ndig++;
1443		nn = n * 10 + (c - '0');
1444		if(nn < n)
1445			over = 1;
1446		n = nn;
1447	}
1448	if(ndig == 0)
1449		return (0, start);
1450	if(neg)
1451		n = -n;
1452	if(over)
1453		if(neg)
1454			n = 2147483647;
1455		else
1456			n = int -2147483648;
1457	return (n, p);
1458}
1459
1460hex(a: array of byte): int
1461{
1462	n := 0;
1463	for(i := 0; i < len a; i++){
1464		c := int a[i];
1465		if(c >= '0' && c <= '9')
1466			c -= '0';
1467		else
1468			c -= 'a' - 10;
1469		n = (n << 4) + (c & 15);
1470	}
1471	return n;
1472}
1473
1474partition(n : int, max : int) : (int, int, int)
1475{
1476	p := n/max;
1477	if (n%max != 0)
1478		p++;
1479	if (p > max)
1480		p = max;
1481	q := n/p;
1482	r := n-p*q;
1483	return (p, q, r);
1484}
1485
1486bounds(s : string) : (int, int)
1487{
1488	lb := int s;
1489	for (i := 0; i < len s; i++)
1490		if (s[i] == '.')
1491			break;
1492	if (i+1 >= len s || s[i] != '.' || s[i+1] != '.')
1493		return (1, 0);
1494	ub := int s[i+2:];
1495	return (lb, ub);
1496}
1497