xref: /inferno-os/appl/cmd/stackv.b (revision 99c84fef96ccd10bb6cabb823384c033090293e9)
1implement Stackv;
2
3include "sys.m";
4	sys: Sys;
5include "draw.m";
6include "debug.m";
7	debug: Debug;
8	Prog, Module, Exp: import debug;
9	Tadt, Tarray, Tbig, Tbyte, Treal,
10	Tfn, Tint, Tlist,
11	Tref, Tstring, Tslice: import Debug;
12include "arg.m";
13include "bufio.m";
14	bufio: Bufio;
15	Iobuf: import bufio;
16
17stderr: ref Sys->FD;
18stdout: ref Iobuf;
19
20hasht := array[97] of (int, array of int);
21
22Stackv: module {
23	init: fn(ctxt: ref Draw->Context, argv: list of string);
24};
25
26maxrecur := 16r7ffffffe;
27
28badmodule(p: string)
29{
30	sys->fprint(stderr, "stackv: cannot load %q: %r\n", p);
31	raise "fail:bad module";
32}
33
34currp: ref Prog;
35showtypes := 1;
36showsource := 0;
37showmodule := 0;
38
39init(nil: ref Draw->Context, argv: list of string)
40{
41
42	sys = load Sys Sys->PATH;
43	stderr = sys->fildes(2);
44	debug = load Debug Debug->PATH;
45	if(debug == nil)
46		badmodule(Debug->PATH);
47	bufio = load Bufio Bufio->PATH;
48	if (bufio == nil)
49		badmodule(Bufio->PATH);
50	arg := load Arg Arg->PATH;
51	if (arg == nil)
52		badmodule(Arg->PATH);
53	stdout = bufio->fopen(sys->fildes(1), Sys->OWRITE);
54
55	arg->init(argv);
56	arg->setusage("stackv [-Tlm] [-r maxdepth] [-s dis sbl]... [pid[.sym]...] ...");
57	sblfile := "";
58	while((opt := arg->opt()) != 0){
59		case opt {
60		's' =>
61			arg->earg();	# XXX make it a list of maps from dis to sbl later
62			sblfile = arg->earg();
63		'l' =>
64			showsource = 1;
65		'm' =>
66			showmodule = 1;
67		'r' =>
68			maxrecur = int arg->earg();
69		'T' =>
70			showtypes = 0;
71		* =>
72			arg->usage();
73		}
74	}
75	debug->init();
76	argv = arg->argv();
77	printpids := len argv > 1;
78	if(printpids)
79		maxrecur++;
80	for(; argv != nil; argv = tl argv)
81		db(sys->tokenize(hd argv, ".").t1, printpids);
82}
83
84db(toks: list of string, printpid: int): int
85{
86	if(toks == nil){
87		sys->fprint(stderr, "stackv: bad pid\n");
88		return -1;
89	}
90	if((pid := int hd toks) <= 0){
91		sys->fprint(stderr, "stackv: bad pid %q\n", hd toks);
92		return -1;
93	}
94	err: string;
95	p: ref Prog;
96
97	# reuse process if possible
98	if(currp == nil || currp.id != pid){
99		(currp, err) = debug->prog(pid);
100		if(err != nil){
101			sys->fprint(stderr, "stackv: %s\n", err);
102			return -1;
103		}
104		if(currp == nil){
105			sys->fprint(stderr, "stackv: nil prog from pid %d\n", pid);
106			return -1;
107		}
108	}
109	p = currp;
110	stk: array of ref Exp;
111	(stk, err) = p.stack();
112	if(err != nil){
113		sys->fprint(stderr, "stackv: %s\n", err);
114		return -1;
115	}
116	for (i := 0; i < len stk; i++) {
117		stk[i].m.stdsym();
118		stk[i].findsym();
119	}
120	depth := 0;
121	if(printpid){
122		stdout.puts(sys->sprint("prog %d {\n", pid));	# }
123		depth++;
124	}
125	pexp(stk, tl toks, depth);
126	if(printpid)
127		stdout.puts("}\n");
128	stdout.flush();
129	return 0;
130}
131
132pexp(stk: array of ref Exp, toks: list of string, depth: int)
133{
134	if(toks == nil){
135		for (i := 0; i < len stk; i++)
136			pfn(stk[i], depth);
137	}else{
138		exp := stackfindsym(stk, toks, depth);
139		if(exp == nil)
140			return;
141		pname(exp, depth, nil);
142		stdout.putc('\n');
143	}
144}
145
146stackfindsym(stk: array of ref Exp, toks: list of string, depth: int): ref Exp
147{
148	fname := hd toks;
149	toks = tl toks;
150	for(i := 0; i < len stk; i++){
151		s := stk[i].name;
152		if(s == fname)
153			break;
154		if(hasdot(s) && toks != nil && s == fname+"."+hd toks){
155			fname += "."+hd toks;
156			toks = tl toks;
157			break;
158		}
159	}
160	if(i == len stk){
161		indent(depth);
162		stdout.puts("function not found\n");
163		return nil;
164	}
165	if(toks == nil)
166		return stk[i];
167	stk = stk[i].expand();
168	if(hd toks == "module"){
169		if((e := getname(stk, "module")) == nil){
170			indent(depth);
171			stdout.puts(sys->sprint("no module declarations in function %q\n", fname));
172		}else if((e = symfindsym(e, tl toks, depth)) != nil)
173			return e;
174		return nil;
175	}
176	for(t := "locals" :: "args" :: "module" :: nil; t != nil; t = tl t){
177		if((e := getname(stk, hd t)) == nil)
178			continue;
179		if((e = symfindsym(e, toks, depth)) != nil)
180			return e;
181	}
182	indent(depth);
183	stdout.puts(sys->sprint("symbol %q not found in function %q\n", hd toks, fname));
184	return nil;
185}
186
187hasdot(s: string): int
188{
189	for(i := 0; i < len s; i++)
190		if(s[i] == '.')
191			return 1;
192	return 0;
193}
194
195symfindsym(e: ref Exp, toks: list of string, depth: int): ref Exp
196{
197	if(toks == nil)
198		return e;
199	exps := e.expand();
200	for(i := 0; i < len exps; i++)
201		if(exps[i].name == hd toks)
202			return symfindsym(exps[i], tl toks, depth);
203	return nil;
204}
205
206pfn(exp: ref Exp, depth: int)
207{
208	(v, w) := exp.val();
209	if(!w || v == nil){
210		indent(depth);
211		stdout.puts(sys->sprint("no value for fn %q\n", exp.name));
212		return;
213	}
214	exps := exp.expand();
215	indent(depth);
216	stdout.puts("["+exp.srcstr()+"]\n");
217	indent(depth);
218	stdout.puts(symname(exp)+"(");
219	if((e := getname(exps, "args")) != nil){
220		args := e.expand();
221		for(i := 0; i < len args; i++){
222			pname(args[i], depth+1, nil);
223			if(i != len args - 1)
224				stdout.puts(", ");
225		}
226	}
227	stdout.puts(")\n");
228	indent(depth);
229	stdout.puts("{\n");	# }
230	if((e = getname(exps, "locals")) != nil){
231		locals := e.expand();
232		for(i := 0; i < len locals; i++){
233			indent(depth+1);
234			pname(locals[i], depth+1, nil);
235			stdout.puts("\n");
236		}
237	}
238	if(showmodule && (e = getname(exps, "module")) != nil){
239		mvars := e.expand();
240		for(i := 0; i < len mvars; i++){
241			indent(depth+1);
242			pname(mvars[i], depth+1, "module.");
243			stdout.puts("\n");
244		}
245	}
246	indent(depth);
247	stdout.puts("}\n");
248}
249
250getname(exps: array of ref Exp, name: string): ref Exp
251{
252	for(i := 0; i < len exps; i++)
253		if(exps[i].name == name)
254			return exps[i];
255	return nil;
256}
257
258strval(v: string): string
259{
260	for(i := 0; i < len v; i++)
261		if(v[i] == '"')
262			break;
263	if(i < len v)
264		v = v[i:];
265	return v;
266}
267
268pname(exp: ref Exp, depth: int, prefix: string)
269{
270	name := prefix+symname(exp);
271	(v, w) := exp.val();
272	if (!w && v == nil) {
273		stdout.puts(sys->sprint("%s: %s = novalue", symname(exp), exp.typename()));
274		return;
275	}
276	case exp.kind() {
277	Tfn =>
278		pfn(exp, depth);
279	Tint =>
280		stdout.puts(sys->sprint("%s := %s", name, v));
281	Tstring =>
282		stdout.puts(sys->sprint("%s := %s", name, strval(v)));
283	Tbyte or
284	Tbig or
285	Treal =>
286		stdout.puts(sys->sprint("%s := %s %s", name, exp.typename(), v));
287	* =>
288		if(showtypes)
289			stdout.puts(sys->sprint("%s: %s = ", name, exp.typename()));
290		else
291			stdout.puts(sys->sprint("%s := ", name));
292		pval(exp, v, w, depth);
293	}
294}
295
296srcstr(src: ref Debug->Src): string
297{
298	if(src == nil)
299		return nil;
300	if(src.start.file != src.stop.file)
301		return sys->sprint("%q:%d.%d,%q:%d.%d", src.start.file, src.start.line, src.start.pos, src.stop.file, src.stop.line, src.stop.pos);
302	if(src.start.line != src.stop.line)
303		return sys->sprint("%q:%d.%d,%d.%d", src.start.file, src.start.line, src.start.pos, src.stop.line, src.stop.pos);
304	return sys->sprint("%q:%d.%d,%d", src.start.file, src.start.line, src.start.pos, src.stop.pos);
305}
306
307pval(exp: ref Exp, v: string, w: int, depth: int)
308{
309	if(depth >= maxrecur){
310		stdout.puts(v);
311		return;
312	}
313	case exp.kind() {
314	Tarray =>
315		if(pref(v)){
316			if(depth+1 >= maxrecur)
317				stdout.puts(v+"{...}");
318			else{
319				stdout.puts(v+"{\n");
320				indent(depth+1);
321				parray(exp, depth+1);
322				stdout.puts("\n");
323				indent(depth);
324				stdout.puts("}");
325			}
326		}
327	Tlist =>
328		if(v == "nil")
329			stdout.puts("nil");
330		else
331		if(depth+1 >= maxrecur)
332			stdout.puts(v+"{...}");
333		else{
334			stdout.puts("{\n");
335			indent(depth+1);
336			plist(exp, v, w, depth+1);
337			stdout.puts("\n");
338			indent(depth);
339			stdout.puts("}");
340		}
341	Tadt =>
342		pgenval(exp, nil, w, depth);
343	Tref =>
344		if(pref(v))
345			pgenval(exp, v, w, depth);
346	Tstring =>
347		stdout.puts(strval(v));
348	* =>
349		pgenval(exp, v, w, depth);
350	}
351}
352
353parray(exp: ref Exp, depth: int)
354{
355	exps := exp.expand();
356	for(i := 0; i < len exps; i++){
357		e := exps[i];
358		(v, w) := e.val();
359		if(e.kind() == Tslice)
360			parray(e, depth);
361		else{
362			pval(e, v, w, depth);
363			stdout.puts(", ");
364		}
365	}
366}
367
368plist(exp: ref Exp, v: string, w: int, depth: int)
369{
370	while(w && v != "nil"){
371		exps := exp.expand();
372		h := getname(exps, "hd");
373		if(h == nil)
374			break;
375		(hv, vw) := h.val();
376		if(pref(v) == 0)
377			return;
378		stdout.puts(v+"(");
379		pval(h, hv, vw, depth);
380		stdout.puts(") :: ");
381		h = nil;
382		exp = getname(exps, "tl");
383		(v, w) = exp.val();
384	}
385	stdout.puts("nil");
386}
387
388pgenval(exp: ref Exp, v: string, w: int, depth: int)
389{
390	if(w){
391		exps := exp.expand();
392		if(len exps == 0)
393			stdout.puts(v);
394		else{
395			stdout.puts(v+"{\n");		# }
396			if (len exps > 0){
397				if(depth >= maxrecur){
398					indent(depth);
399					stdout.puts(sys->sprint("...[%d]\n", len exps));
400				}else{
401					for (i := 0; i < len exps; i++){
402						indent(depth+1);
403						pname(exps[i], depth+1, nil);
404						stdout.puts("\n");
405					}
406				}
407			}
408			indent(depth);		# {
409			stdout.puts("}");
410		}
411	}else
412		stdout.puts(v);
413}
414
415symname(exp: ref Exp): string
416{
417	if(showsource == 0)
418		return exp.name;
419	return exp.name+"["+srcstr(exp.src())+"]";
420}
421
422indent(n: int)
423{
424	while(n-- > 0)
425		stdout.putc('\t');
426}
427
428ref2int(v: string): int
429{
430	if(v == nil)
431		error("bad empty value for ref");
432	i := 0;
433	n := len v;
434	if(v[0] == '@')
435		i = 1;
436	else{
437		# skip array bounds
438		if(v[0] == '['){
439			for(; i < n && v[i] != ']'; i++)
440				;
441			if(i >= n - 2 || v[i+1] != ' ' || v[i+2] != '@')
442				error("bad value for ref: "+v);
443			i += 3;
444		}
445	}
446	if(n - i > 8)
447		error("64-bit pointers?");
448	p := 0;
449	for(; i < n; i++){
450		c := v[i];
451		case c {
452		'0' to '9' =>
453			p = (p << 4) + (c - '0');
454		'a' to 'f' =>
455			p = (p << 4) + (c - 'a' + 10);
456		* =>
457			error("bad value for ref: "+v);
458		}
459	}
460	return p;
461}
462
463pref(v: string): int
464{
465	if(v == "nil"){
466		stdout.puts("nil");
467		return 0;
468	}
469	if(addref(ref2int(v)) == 0){
470		stdout.puts(v);
471		stdout.puts("(qv)");
472		return 0;
473	}
474	return 1;
475}
476
477# hash table implementation that tries to be reasonably
478# parsimonious on memory usage.
479addref(v: int): int
480{
481	slot := (v & 16r7fffffff) % len hasht;
482	(n, a) := hasht[slot];
483	for(i := 0; i < n; i++)
484		if(a[i] == v)
485			return 0;
486	if(n == len a){
487		if(n == 0)
488			n = 3;
489		t := array[n*3/2] of int;
490		t[0:] = a;
491		hasht[slot].t1 = t;
492		a = t;
493	}
494	a[hasht[slot].t0++] = v;
495	return 1;
496}
497
498error(e: string)
499{
500	sys->fprint(sys->fildes(2), "stackv: error: %s\n", e);
501	raise "fail:error";
502}
503