xref: /inferno-os/appl/lib/profile.b (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
1implement Profile;
2
3include "sys.m";
4	sys: Sys;
5include "draw.m";
6include "bufio.m";
7	bufio: Bufio;
8	Iobuf: import bufio;
9include "workdir.m";
10	workdir: Workdir;
11include "debug.m";
12	debug: Debug;
13	Sym: import debug;
14include "dis.m";
15	dism: Dis;
16include "profile.m";
17
18# merge common code
19
20PROF: con "/prof";
21CTL: con "ctl";
22NAME: con "name";
23MPATH: con "path";
24HISTOGRAM: con "histogram";
25
26inited: int;
27modl: string;
28lasterr: string;
29
30bspath := array[] of
31{
32	("/dis/",		"/appl/cmd/"),
33	("/dis/",		"/appl/"),
34};
35
36error(s: string)
37{
38	lasterr = sys->sprint("%s: %r", s);
39}
40
41error0(s: string)
42{
43	lasterr = s;
44}
45
46cleare()
47{
48	lasterr = nil;
49}
50
51lasterror(): string
52{
53	return lasterr;
54}
55
56init(): int
57{
58	cleare();
59	sys = load Sys Sys->PATH;
60	bufio = load Bufio Bufio->PATH;
61	debug = load Debug Debug->PATH;
62	if(debug == nil){
63		error("cannot load Debug module");
64		return -1;
65	}
66	debug->init();
67	(ok, nil) := sys->stat(PROF + "/ctl");
68	if (ok == -1) {
69		if(sys->bind("#P", PROF, Sys->MREPL|Sys->MCREATE) < 0){
70			error(sys->sprint("cannot bind prof device to /prof"));
71			return -1;
72		}
73	}
74	inited = 1;
75	return 0;
76}
77
78end(): int
79{
80	cleare();
81	inited = 0;
82	modl = nil;
83	if(write(mkpath(PROF, CTL), "end") < 0)
84		return -1;
85	return 0;
86}
87
88start(): int
89{
90	cleare();
91	if(!inited && init() < 0)
92		return -1;
93	if(write(mkpath(PROF, CTL), "module " + modl) < 0)
94		return -1;
95	if(write(mkpath(PROF, CTL), "start") < 0)
96		return -1;
97	return 0;
98}
99
100cpstart(pid: int): int
101{
102	cleare();
103	if(!inited && init() < 0)
104		return -1;
105	if(write(mkpath(PROF, CTL), "module " + modl) < 0)
106		return -1;
107	if(write(mkpath(PROF, CTL), "startcp " + string pid) < 0)
108		return -1;
109	return 0;
110}
111
112memstart(m: int): int
113{
114	cleare();
115	if(!inited && init() < 0)
116		return -1;
117	if(modl != nil && write(mkpath(PROF, CTL), "module " + modl) < 0)
118		return -1;
119	start := "startmp";
120	if(m == 0)
121		m = MAIN|HEAP|IMAGE;
122	if(m&MAIN)
123		start += "1";
124	if(m&HEAP)
125		start += "2";
126	if(m&IMAGE)
127		start += "3";
128	if(write(mkpath(PROF, CTL), start) < 0)
129		return -1;
130	return 0;
131}
132
133stop(): int
134{
135	cleare();
136	if(!inited && init() < 0)
137		return -1;
138	if(write(mkpath(PROF, CTL), "stop") < 0)
139		return -1;
140	return 0;
141}
142
143sample(i: int): int
144{
145	cleare();
146	if(i <= 0){
147		error0(sys->sprint("bad sample rate %d", i));
148		return -1;
149	}
150	if(write(mkpath(PROF, CTL), "interval " + string i) < 0)
151		return -1;
152	return 0;
153}
154
155profile(m: string): int
156{
157	cleare();
158	modl = m + " " + modl;
159	return 0;
160}
161
162stats(): Prof
163{
164	mp: Modprof;
165	p: Prof;
166	mpl: list of Modprof;
167
168	cleare();
169	fd := sys->open(PROF, Sys->OREAD);
170	if(fd == nil){
171		error(sys->sprint("cannot open %s for reading", PROF));
172		return (nil, 0, nil);
173	}
174	total := 0;
175	for(;;){
176		(nr, d) := sys->dirread(fd);
177		if(nr <= 0)
178			break;
179		for(i := 0; i < nr; i++){
180			if(d[i].name == CTL)
181				continue;
182			dn := mkpath(PROF, d[i].name);
183			mp.name = read(mkpath(dn, NAME));
184			mp.path = read(mkpath(dn, MPATH));
185			fdh := sys->open(mkpath(dn, HISTOGRAM), Sys->OREAD);
186			if(fdh == nil)
187				continue;
188			(mp.srcpath, mp.linetab, mp.funtab, mp.total) = tprofile(fdh, mp.path);
189			if((sp := getb(mp.path)) != nil)
190				mp.srcpath = sp;
191			if(mp.total != 0){
192				mpl = mp :: mpl;
193				total += mp.total;
194			}
195		}
196	}
197	p.mods = mpl;
198	p.total = total;
199	return p;
200}
201
202cpstats(rec: int, v: int): Prof
203{
204	m: string;
205	mp: Modprof;
206	p: Prof;
207	mpl: list of Modprof;
208
209	cleare();
210	fd := sys->open(PROF, Sys->OREAD);
211	if(fd == nil){
212		error(sys->sprint("cannot open %s for reading", PROF));
213		return (nil, 0, nil);
214	}
215	total := 0;
216	for(;;){
217		(nr, d) := sys->dirread(fd);
218		if(nr <= 0)
219			break;
220		for(i:=0; i<nr; i++){
221			if(d[i].name == CTL)
222				continue;
223			dn := mkpath(PROF, d[i].name);
224			mp.name = read(mkpath(dn, NAME));
225			mp.path = read(mkpath(dn, MPATH));
226			fdh := sys->open(mkpath(dn, HISTOGRAM), Sys->OREAD);
227			if(fdh == nil)
228				continue;
229			(m, mp.srcpath, mp.rawtab, mp.linetab, mp.rngtab, mp.total, mp.coverage) = cprofile(fdh, mp.path, rec, v);
230			if(mp.name == nil)
231				mp.name = m;
232			if((sp := getb(mp.path)) != nil)
233				mp.srcpath = sp;
234			if(len mp.rawtab > 0){
235				mpl = mp :: mpl;
236				total += mp.total;
237			}
238		}
239	}
240	p.mods = mpl;
241	p.total = total;
242	return p;
243}
244
245cpfstats(v: int): Prof
246{
247	mp: Modprof;
248	p: Prof;
249	mpl: list of Modprof;
250
251	cleare();
252	total := 0;
253	(nil, l) := sys->tokenize(modl, " ");
254	for( ; l != nil; l = tl l){
255		s := hd l;
256		suf := suff(s);
257		if(suf == nil)
258			s += ".dis";
259		else
260			s = repsuff(s, "."+suf, ".dis");
261		if(!exists(s) && s[0] != '/' && s[0:2] != "./")
262			s = "/dis/"+s;
263		mp.path = s;
264		(mp.name, mp.srcpath, mp.rawtab, mp.linetab, mp.rngtab, mp.total, mp.coverage) = cprofile(nil, mp.path, 1, v);
265		if((sp := getb(mp.path)) != nil)
266			mp.srcpath = sp;
267		if(len mp.rawtab > 0){
268			mpl = mp :: mpl;
269			total += mp.total;
270		}
271	}
272	p.mods = mpl;
273	p.total = total;
274	return p;
275}
276
277memstats(): Prof
278{
279	mp: Modprof;
280	p: Prof;
281	mpl: list of Modprof;
282
283	cleare();
284	fd := sys->open(PROF, Sys->OREAD);
285	if(fd == nil){
286		error(sys->sprint("cannot open %s for reading", PROF));
287		return (nil, 0, nil);
288	}
289	total := totale := 0;
290	for(;;){
291		(nr, d) := sys->dirread(fd);
292		if(nr <= 0)
293			break;
294		for(i:=0; i<nr; i++){
295			if(d[i].name == CTL)
296				continue;
297			dn := mkpath(PROF, d[i].name);
298			mp.name = read(mkpath(dn, NAME));
299			mp.path = read(mkpath(dn, MPATH));
300			fdh := sys->open(mkpath(dn, HISTOGRAM), Sys->OREAD);
301			if(fdh == nil)
302				continue;
303			mp.totals = array[1] of int;
304			(mp.srcpath, mp.linetab, mp.funtab, mp.total, mp.totals[0]) = mprofile(fdh, mp.path);
305			if((sp := getb(mp.path)) != nil)
306				mp.srcpath = sp;
307			if(mp.total != 0 || mp.totals[0] != 0){
308				mpl = mp :: mpl;
309				total += mp.total;
310				totale += mp.totals[0];
311			}
312		}
313	}
314	p.mods = mpl;
315	p.total = total;
316	p.totals = array[1] of int;
317	p.totals[0] = totale;
318	return p;
319}
320
321tprofile(fd: ref Sys->FD, dis: string): (string, array of int, array of Funprof, int)
322{
323	sbl := findsbl(dis);
324	if(sbl == nil){
325		error0(sys->sprint("cannot locate symbol table file for %s", dis));
326		return (nil, nil, nil, 0);
327	}
328	(sym, err) := debug->sym(sbl);
329	if(sym == nil){
330		error0(sys->sprint("bad symbol table file: %s", err));
331		return (nil, nil, nil, 0);
332	}
333	nlines := 0;
334	nl := len sym.src;
335	for(i := 0; i < nl; i++){
336		if((l := sym.src[i].stop.line) > nlines)
337			nlines = l;
338	}
339	name := sym.src[0].start.file;
340	line := array[nlines+1] of int;
341	for(i = 0; i <= nlines; i++)
342		line[i] = 0;
343	nf := len sym.fns;
344	fun := array[nf] of Funprof;
345	for(i = 0; i < nf; i++){
346		fun[i].name = sym.fns[i].name;
347		# src seems to be always nil
348		# fun[i].file = sym.fns[i].src.start.file;
349		# fun[i].line = (sym.fns[i].src.start.line+sym.fns[i].src.stop.line)/2;
350		src := sym.pctosrc(sym.fns[i].offset);
351		if(src != nil)
352			fun[i].line = src.start.line;
353		else
354			fun[i].line = 0;
355		fun[i].count = 0;
356	}
357	buf := array[32] of byte;
358	# pc := 0;
359	tot := 0;
360	fi := 0;
361# for(i=0; i < nl; i++) sys->print("%d -> %d\n", i, sym.pctosrc(i).start.line);
362	while((m := sys->read(fd, buf, len buf)) > 0){
363		(nw, lw) := sys->tokenize(string buf[0:m], " ");
364		if(nw != 2){
365			error0("bad histogram data");
366			return  (nil, nil, nil, 0);
367		}
368		pc := int hd lw;
369		f := int hd tl lw;
370		rpc := pc-1;
371		src := sym.pctosrc(rpc);
372		if(src == nil)
373			continue;
374		l1 := src.start.line;
375		l2 := src.stop.line;
376		if(l1 == 0 || l2 == 0)
377			continue;
378		if((nl = l2-l1+1) == 1)
379			line[l1] += f;
380		else{
381			q := f/nl;
382			r := f-q*nl;
383			for(i = l1; i <= l2; i++)
384				line[i] += q+(r-->0);
385		}
386		if(fi < nf){
387			if(rpc >= sym.fns[fi].offset && rpc < sym.fns[fi].stoppc)
388				fun[fi].count += f;
389			else{
390				while(fi < nf && rpc >= sym.fns[fi].stoppc)
391					fi++;
392				# fi++;
393				if(fi >= nf && f != 0)
394					error0(sys->sprint("bad fn index"));
395				if(fi < nf)
396					fun[fi].count += f;
397			}
398		}
399		tot += f;
400# sys->print("pc %d count %d l1 %d l2 %d\n", rpc, f, l1, l2);
401	}
402	return (name, line, fun, tot);
403}
404
405cprofile(fd: ref Sys->FD, dis: string, rec: int, v: int): (string, string, array of (int, int), array of int, array of ref Range, int, int)
406{
407	freq := v&FREQUENCY;
408	sbl := findsbl(dis);
409	if(sbl == nil){
410		error0(sys->sprint("cannot locate symbol table file for %s", dis));
411		return (nil, nil, nil, nil, nil, 0, 0);
412	}
413	(sym, err) := debug->sym(sbl);
414	if(sym == nil){
415		error0(sys->sprint("bad symbol table file: %s", err));
416		return (nil, nil, nil, nil, nil, 0, 0);
417	}
418	nlines := 0;
419	nl := len sym.src;
420	for(i := 0; i < nl; i++){
421		if((l := sym.src[i].start.line) > nlines)
422			nlines = l;
423		if((l = sym.src[i].stop.line) > nlines)
424			nlines = l;
425	}
426	name := sym.src[0].start.file;
427	line := array[nlines+1] of int;
428	for(i = 0; i <= nlines; i++){
429		if(freq)
430			line[i] = -1;
431		else
432			line[i] = 0;
433	}
434	rng := array[nlines+1] of ref Range;
435	for(i = 0; i < nl; i++)
436		cover(i, -1, sym, line, rng, freq);
437	buf := array[32] of byte;
438	nr := 0;
439	r := array[1024] of (int, int);
440	while((m := sys->read(fd, buf, len buf)) > 0){
441		(nw, lw) := sys->tokenize(string buf[0:m], " ");
442		if(nw != 2){
443			error0("bad histogram data");
444			return  (nil, nil, nil, nil, nil, 0, 0);
445		}
446		(r, nr) = add(r, nr, int hd lw, int hd tl lw);
447	}
448	r = clip(r, nr);
449	if(rec){
450		wt := nr > 0;
451		prf := repsuff(sbl, ".sbl", ".prf");
452		if(exists(prf)){
453			if(stamp(sbl) > stamp(prf)){
454				error0(sys->sprint("%s later than %s", sbl, prf));
455				return (nil, nil, nil, nil, nil, 0, 0);
456			}
457			r = mergeprof(r, readprof(prf));
458			nr = len r;
459		}
460		if(wt && writeprof(prf, r) < 0){
461			error0(sys->sprint("cannot write profile file %s", prf));
462			return (nil, nil, nil, nil, nil, 0, 0);
463		}
464	}
465	tot := 0;
466	lpc := 0;
467	dise := dist := 0;
468	for(i = 0; i < nr; i++){
469		(pc, f) := r[i];
470		for( ; lpc < pc; lpc++){
471			cover(lpc, 0, sym, line, rng, freq);
472			dist++;
473		}
474		cover(pc, f, sym, line, rng, freq);
475		dist++;
476		if(f != 0)
477			dise++;
478		tot += f;
479		lpc = pc+1;
480	}
481	for( ; lpc < nl; lpc++){
482		cover(lpc, 0, sym, line, rng, freq);
483		dist++;
484	}
485	if(dist == 0)
486		dist = 1;
487	return (sym.name, name, r, line, rng, tot, (100*dise)/dist);
488}
489
490show(p: Prof, v: int): int
491{
492	i: int;
493
494	cleare();
495	tot := p.total;
496	if(tot == 0)
497		return 0;
498	verbose := v&VERBOSE;
499	fullhdr := v&FULLHDR;
500	for(ml := p.mods; ml != nil; ml = tl ml){
501		mp := hd ml;
502		if(mp.total == 0)
503			continue;
504		if((b := getb(mp.path)) == nil)
505			continue;
506		sys->print("\nModule: %s(%s)\n\n", mp.name, mp.path);
507		line := mp.linetab;
508		if(v&FUNCTION){
509			fun := mp.funtab;
510			nf := len fun;
511			for(i = 0; i < nf; i++)
512				if(verbose || fun[i].count != 0){
513					if(fullhdr)
514						sys->print("%s:", b);
515					sys->print("%d\t%.2f\t%s()\n", fun[i].line, 100.0*(real fun[i].count)/(real tot), fun[i].name);
516			}
517			sys->print("\n**** module sampling points %d ****\n\n", mp.total);
518			if(v&LINE)
519				sys->print("\n");
520		}
521		if(v&LINE){
522			bio := bufio->open(b, Bufio->OREAD);
523			if(bio == nil){
524				error(sys->sprint("cannot open %s for reading", b));
525				continue;
526			}
527			i = 1;
528			ll := len line;
529			while((s := bio.gets('\n')) != nil){
530				f := 0;
531				if(i < ll)
532					f = line[i];
533				if(verbose || f != 0){
534					if(fullhdr)
535						sys->print("%s:", b);
536					sys->print("%d\t%.2f\t%s", i, 100.0*(real f)/(real tot), s);
537				}
538				i++;
539			}
540			sys->print("\n**** module sampling points %d ****\n\n", mp.total);
541		}
542	}
543	if(p.mods != nil && tl p.mods != nil)
544		sys->print("\n**** total sampling points %d ****\n\n", p.total);
545	return 0;
546}
547
548cpshow(p: Prof, v: int): int
549{
550	i: int;
551
552	cleare();
553	tot := p.total;
554	fullhdr := v&FULLHDR;
555	freq := v&FREQUENCY;
556	for(ml := p.mods; ml != nil; ml = tl ml){
557		mp := hd ml;
558		if((b := getb(mp.path)) == nil)
559			continue;
560		sys->print("\nModule: %s(%s)", mp.name, mp.path);
561		sys->print("\t%d%% coverage\n\n", mp.coverage);
562		if(mp.coverage == 100 && !freq)
563			continue;
564		line := mp.linetab;
565		rng := mp.rngtab;
566		bio := bufio->open(b, Bufio->OREAD);
567		if(bio == nil){
568			error(sys->sprint("cannot open %s for reading", b));
569			continue;
570		}
571		i = 1;
572		ll := len line;
573		while((s := bio.gets('\n')) != nil){
574			f := 0;
575			if(i < ll)
576				f = line[i];
577			if(fullhdr)
578				sys->print("%s:", b);
579			sys->print("%d\t", i);
580			if(rng != nil && i < ll && (r := rng[i]) != nil && multirng(r)){
581				for( ; r != nil; r = r.n){
582					sys->print("%s", trans(r.f, freq));
583					if(r.n != nil)
584						sys->print("|");
585				}
586			}
587			else
588				sys->print("%s", trans(f, freq));
589			sys->print("\t%s", s);
590			i++;
591		}
592		sys->print("\n**** module dis instructions %d ****\n\n", mp.total);
593	}
594	if(p.mods != nil && tl p.mods != nil)
595		sys->print("\n**** total number dis instructions %d ****\n\n", p.total);
596	return 0;
597}
598
599coverage(p: Prof, v: int): Coverage
600{
601	i: int;
602	clist: Coverage;
603
604	cleare();
605	freq := v&FREQUENCY;
606	for(ml := p.mods; ml != nil; ml = tl ml){
607		mp := hd ml;
608		if((b := getb(mp.path)) == nil)
609			continue;
610		line := mp.linetab;
611		rng := mp.rngtab;
612		bio := bufio->open(b, Bufio->OREAD);
613		if(bio == nil){
614			error(sys->sprint("cannot open %s for reading", b));
615			continue;
616		}
617		i = 1;
618		ll := len line;
619		llist: list of (list of (int, int, int), string);
620		while((s := bio.gets('\n')) != nil){
621			f := 0;
622			if(i < ll)
623				f = line[i];
624			rlist: list of (int, int, int);
625			if(rng != nil && i < ll && (r := rng[i]) != nil){
626				for( ; r != nil; r = r.n){
627					if(r.u == ∞)
628						r.u = len s - 1;
629					if(freq){
630						if(r.f > 0)
631							rlist = (r.l, r.u, r.f) :: rlist;
632					}
633					else{
634						if(r.f&NEX)
635							rlist = (r.l, r.u, (r.f&EXE)==EXE) :: rlist;
636					}
637				}
638			}
639			else{
640				if(freq){
641					if(f > 0)
642						rlist = (0, len s - 1, f) :: rlist;
643				}
644				else{
645					if(f&NEX)
646						rlist = (0, len s - 1, (f&EXE)==EXE) :: nil;
647				}
648			}
649			llist = (rlist, s) :: llist;
650			i++;
651		}
652		if(freq)
653			n := mp.total;
654		else
655			n = mp.coverage;
656		clist = (b, n, rev(llist)) :: clist;
657	}
658	return clist;
659}
660
661∞: con 1<<30;
662
663DIS: con 1;
664EXE: con 2;
665NEX: con 4;
666
667cover(pc: int, f: int, sym: ref Debug->Sym, line: array of int, rng: array of ref Range, freq: int)
668{
669	v: int;
670
671	src := sym.pctosrc(pc);
672	if(src == nil)
673		return;
674	l1 := src.start.line;
675	l2 := src.stop.line;
676	if(l1 == 0 || l2 == 0)
677		return;
678	c1 := src.start.pos;
679	c2 := src.stop.pos;
680	if(freq){
681		v = 0;
682		if(f > 0)
683			v = f;
684	}
685	else{
686		v = DIS;
687		if(f > 0)
688			v = EXE;
689		else if(f == 0)
690			v = NEX;
691	}
692	for(i := l1; i <= l2; i++){
693		r1 := 0;
694		r2 := ∞;
695		if(i == l1)
696			r1 = c1;
697		if(i == l2)
698			r2 = c2;
699		if(rng != nil)
700			rng[i] = mrgrng(addrng(rng[i], r1, r2, v, freq));
701		if(freq){
702			if(v > line[i])
703				line[i] = v;
704		}
705		else
706			line[i] |= v;
707		# if(i==123) sys->print("%d %d-%d %d %d\n", i, r1, r2, v, pc);
708	}
709}
710
711arng(c1: int, c2: int, f: int, tr: ref Range, lr: ref Range, r: ref Range): ref Range
712{
713	nr := ref Range(c1, c2, f, tr);
714	if(lr == nil)
715		r = nr;
716	else
717		lr.n = nr;
718	return r;
719}
720
721addrng(r: ref Range, c1: int, c2: int, f: int, freq: int): ref Range
722{
723	lr: ref Range;
724
725	if(c1 > c2)
726		return r;
727	for(tr := r; tr != nil; tr = tr.n){
728		r1 := tr.l;
729		r2 := tr.u;
730		if(c1 < r1){
731			if(c2 < r1)
732				return arng(c1, c2, f, tr, lr, r);
733			else if(c2 <= r2){
734				r = addrng(r, c1, r1-1, f, freq);
735				return addrng(r, r1, c2, f, freq);
736			}
737			else{
738				r = addrng(r, c1, r1-1, f, freq);
739				r = addrng(r, r1, r2, f, freq);
740				return addrng(r, r2+1, c2, f, freq);
741			}
742		}
743		else if(c1 <= r2){
744			if(c2 <= r2){
745				v := tr.f;
746				tr.l = c1;
747				tr.u = c2;
748				if(freq){
749					if(f > tr.f)
750						tr.f = f;
751				}
752				else
753					tr.f |= f;
754				r = addrng(r, r1, c1-1, v, freq);
755				return addrng(r, c2+1, r2, v, freq);
756			}
757			else{
758				r = addrng(r, c1, r2, f, freq);
759				return addrng(r, r2+1, c2, f, freq);
760			}
761		}
762		lr = tr;
763	}
764	return arng(c1, c2, f, nil, lr, r);
765}
766
767mrgrng(r: ref Range): ref Range
768{
769	lr: ref Range;
770
771	for(tr := r; tr != nil; tr = tr.n){
772		if(lr != nil && lr.u >= tr.l)
773			sys->print("ERROR %d %d\n", lr.u, tr.l);
774		if(lr != nil && lr.f == tr.f && lr.u+1 == tr.l){
775			lr.u = tr.u;
776			lr.n = tr.n;
777		}
778		else
779			lr = tr;
780	}
781	return r;
782}
783
784multirng(r: ref Range): int
785{
786	f := r.f;
787	for(tr := r; tr != nil; tr = tr.n)
788		if(tr.f != f)
789			return 1;
790	return 0;
791}
792
793add(r: array of (int, int), nr: int, pc: int, f: int): (array of (int, int), int)
794{
795	l := len r;
796	if(nr == l){
797		s := array[2*l] of (int, int);
798		s[0:] = r[0: nr];
799		r = s;
800	}
801	r[nr++] = (pc, f);
802	return (r, nr);
803}
804
805clip(r: array of (int, int), nr: int): array of (int, int)
806{
807	l := len r;
808	if(nr < l){
809		s := array[nr] of (int, int);
810		s[0:] = r[0: nr];
811		r = s;
812	}
813	return r;
814}
815
816readprof(f: string): array of (int, int)
817{
818	b := bufio->open(f, Bufio->OREAD);
819	if(b == nil)
820		return nil;
821	nr := 0;
822	r := array[1024] of (int, int);
823	while((buf := b.gets('\n')) != nil){
824		(nw, lw) := sys->tokenize(buf, " ");
825		if(nw != 2){
826			error0("bad raw data");
827			return  nil;
828		}
829		(r, nr) = add(r, nr, int hd lw, int hd tl lw);
830	}
831	r = clip(r, nr);
832	return r;
833}
834
835mergeprof(r1, r2: array of (int, int)): array of (int, int)
836{
837	nr := 0;
838	r := array[1024] of (int, int);
839	l1 := len r1;
840	l2 := len r2;
841	for((i, j) := (0, 0); i < l1 || j < l2; ){
842		if(i < l1)
843			(pc1, f1) := r1[i];
844		else
845			pc1 = ∞;
846		if(j < l2)
847			(pc2, f2) := r2[j];
848		else
849			pc2 = ∞;
850		if(pc1 < pc2){
851			(r, nr) = add(r, nr, pc1, f1);
852			i++;
853		}
854		else if(pc1 > pc2){
855			(r, nr) = add(r, nr, pc2, f2);
856			j++;
857		}
858		else{
859			(r, nr) = add(r, nr, pc1, f1+f2);
860			i++;
861			j++;
862		}
863	}
864	r = clip(r, nr);
865	return r;
866}
867
868writeprof(f: string, r: array of (int, int)): int
869{
870	fd := sys->create(f, Sys->OWRITE, 8r664);
871	if(fd == nil)
872		return -1;
873	l := len r;
874	for(i := 0; i < l; i++){
875		(pc, fr) := r[i];
876		sys->fprint(fd, "%d %d\n", pc, fr);
877	}
878	return 0;
879}
880
881trans(f: int, freq: int): string
882{
883	if(freq)
884		return transf(f);
885	else
886		return transc(f);
887}
888
889transf(f: int): string
890{
891	if(f < 0)
892		return " ";
893	return string f;
894}
895
896transc(f: int): string
897{
898	c := "";
899	case(f){
900		0 => c = " ";
901		DIS|EXE => c = "+";
902		DIS|NEX => c = "-";
903		DIS|EXE|NEX => c = "?";
904		* =>
905			error(sys->sprint("bad code %d\n", f));
906	}
907	return c;
908}
909
910getb(dis: string): string
911{
912	b := findb(dis);
913	if(b == nil){
914		error0(sys->sprint("cannot locate source file for %s\n", dis));
915		return nil;
916	}
917	if(stamp(b) > stamp(dis)){
918		error0(sys->sprint("%s later than %s", b, dis));
919		return nil;
920	}
921	return b;
922}
923
924mkpath(d: string, f: string): string
925{
926	return d+"/"+f;
927}
928
929suff(s: string): string
930{
931	(n, l) := sys->tokenize(s, ".");
932	if(n > 1){
933		while(tl l != nil)
934			l = tl l;
935		return hd l;
936	}
937	return nil;
938}
939
940repsuff(s: string, old: string, new: string): string
941{
942	lo := len old;
943	ls := len s;
944	if(lo <= ls && s[ls-lo:ls] == old)
945		return s[0:ls-lo]+new;
946	return s;
947}
948
949read(f: string): string
950{
951	if((fd := sys->open(f, Sys->OREAD)) == nil){
952		error(sys->sprint("cannot open %s for reading", f));
953		return nil;
954	}
955	buf := array[128] of byte;
956	n := sys->read(fd, buf, len buf);
957	return string buf[0:n];
958}
959
960write(f: string, s: string): int
961{
962	if((fd := sys->open(f, Sys->OWRITE)) == nil){
963		error(sys->sprint("cannot open %s for writing", f));
964		return -1;
965	}
966	b := array of byte s;
967	if((n := sys->write(fd, b, len b)) != len b){
968		error(sys->sprint("cannot write %s to file %s", s, f));
969		return -1;
970	}
971	return 0;
972}
973
974exists(f: string): int
975{
976	return sys->open(f, Sys->OREAD) != nil;
977}
978
979stamp(f: string): int
980{
981	(ok, d) := sys->stat(f);
982	if(ok < 0)
983		return 0;
984	return d.mtime;
985}
986
987findb(dis: string): string
988{
989	if(dism == nil){
990		dism = load Dis Dis->PATH;
991		if(dism != nil)
992			dism->init();
993	}
994	if(dism != nil && (b := dism->src(dis)) != nil && exists(b))
995		return b;
996	return findfile(repsuff(dis, ".dis", ".b"));
997}
998
999findsbl(dis: string): string
1000{
1001	b := findb(dis);
1002	if(b != nil){
1003		sbl := repsuff(b, ".b", ".sbl");
1004		if(exists(sbl))
1005			return sbl;
1006		return findfile(sbl);
1007	}
1008	return findfile(repsuff(dis, ".dis", ".sbl"));
1009}
1010
1011findfile(s: string): string
1012{
1013	if(exists(s))
1014		return s;
1015	if(s != nil && s[0] != '/'){
1016		if(workdir == nil)
1017			workdir = load Workdir Workdir->PATH;
1018		if(workdir == nil){
1019			error("cannot load Workdir module");
1020			return nil;
1021		}
1022		s = workdir->init() + "/" + s;
1023	}
1024	(d, f) := split(s, '/');
1025	(fp, nil) := split(f, '.');
1026	if(fp != nil)
1027		fp = fp[0: len fp - 1];
1028	for(k := 0; k < 2; k++){
1029		if(k == 0)
1030			str := s;
1031		else
1032			str = d;
1033		ls := len str;
1034		for(i := 0; i < len bspath; i++){
1035			(dis, src) := bspath[i];
1036			ld := len dis;
1037			if(ls >= ld && str[:ld] == dis){
1038				if(k == 0)
1039					ns := src + str[ld:];
1040				else
1041					ns = src + str[ld:] + fp + "/" + f;
1042				if(exists(ns))
1043					return ns;
1044			}
1045		}
1046	}
1047	return nil;
1048}
1049
1050split(s: string, c: int): (string, string)
1051{
1052	for(i := len s - 1; i >= 0; --i)
1053		if(s[i] == c)
1054			break;
1055	return (s[0:i+1], s[i+1:]);
1056}
1057
1058rev(llist: list of (list of (int, int, int), string)): list of (list of (int, int, int), string)
1059{
1060	r: list of (list of (int, int, int), string);
1061
1062	for(l := llist; l != nil; l = tl l)
1063		r = hd l :: r;
1064	return r;
1065}
1066
1067mprofile(fd: ref Sys->FD, dis: string): (string, array of int, array of Funprof, int, int)
1068{
1069	sbl := findsbl(dis);
1070	if(sbl == nil){
1071		error0(sys->sprint("cannot locate symbol table file for %s", dis));
1072		return (nil, nil, nil, 0, 0);
1073	}
1074	(sym, err) := debug->sym(sbl);
1075	if(sym == nil){
1076		error0(sys->sprint("bad symbol table file: %s", err));
1077		return (nil, nil, nil, 0, 0);
1078	}
1079	nlines := 0;
1080	nl := len sym.src;
1081	for(i := 0; i < nl; i++){
1082		if((l := sym.src[i].stop.line) > nlines)
1083			nlines = l;
1084	}
1085	name := sym.src[0].start.file;
1086	nl0 := 2*(nlines+1);
1087	line := array[nl0] of int;
1088	for(i = 0; i < nl0; i++)
1089		line[i] = 0;
1090	nf := len sym.fns;
1091	fun := array[nf] of Funprof;
1092	for(i = 0; i < nf; i++){
1093		fun[i].name = sym.fns[i].name;
1094		# src seems to be always nil
1095		# fun[i].file = sym.fns[i].src.start.file;
1096		# fun[i].line = (sym.fns[i].src.start.line+sym.fns[i].src.stop.line)/2;
1097		src := sym.pctosrc(sym.fns[i].offset);
1098		if(src != nil)
1099			fun[i].line = src.start.line;
1100		else
1101			fun[i].line = 0;
1102		fun[i].count = fun[i].counte = 0;
1103	}
1104	buf := array[32] of byte;
1105	# pc := 0;
1106	ktot := ktot1 := 0;
1107	fi := 0;
1108# for(i=0; i < nl; i++) sys->print("%d -> %d\n", i, sym.pctosrc(i).start.line);
1109	while((m := sys->read(fd, buf, len buf)) > 0){
1110		(nw, lw) := sys->tokenize(string buf[0:m], " ");
1111		if(nw != 2){
1112			error0("bad histogram data");
1113			return  (nil, nil, nil, 0, 0);
1114		}
1115		pc := int hd lw;
1116		f := int hd tl lw;
1117		if(pc == 0){
1118			ktot = f;
1119			continue;
1120		}
1121		if(pc == 1){
1122			ktot1 = f;
1123			continue;
1124		}
1125		pc -= 2;
1126		t := pc&1;
1127		pc /= 2;
1128		rpc := pc-1;
1129		src := sym.pctosrc(rpc);
1130		if(src == nil)
1131			continue;
1132		l1 := src.start.line;
1133		l2 := src.stop.line;
1134		if(l1 == 0 || l2 == 0)
1135			continue;
1136		if((nl = l2-l1+1) == 1)
1137			line[2*l1+t] += f;
1138		else{
1139			q := f/nl;
1140			r := f-q*nl;
1141			for(i = l1; i <= l2; i++)
1142				line[2*i+t] += q+(r-->0);
1143		}
1144		if(fi < nf){
1145			if(rpc >= sym.fns[fi].offset && rpc < sym.fns[fi].stoppc){
1146				if(t)
1147					fun[fi].counte += f;
1148				else
1149					fun[fi].count += f;
1150			}
1151			else{
1152				while(fi < nf && rpc >= sym.fns[fi].stoppc)
1153					fi++;
1154				# fi++;
1155				if(fi >= nf && f != 0)
1156					error0(sys->sprint("bad fn index"));
1157				if(fi < nf){
1158					if(t)
1159						fun[fi].counte += f;
1160					else
1161						fun[fi].count += f;
1162				}
1163			}
1164		}
1165# sys->print("pc %d count %d l1 %d l2 %d\n", rpc, f, l1, l2);
1166	}
1167	return (name, line, fun, ktot, ktot1);
1168}
1169
1170memshow(p: Prof, v: int): int
1171{
1172	i: int;
1173
1174	cleare();
1175	tot := p.total;
1176	if(p.total == 0 && p.totals[0] == 0)
1177		return 0;
1178	verbose := v&VERBOSE;
1179	fullhdr := v&FULLHDR;
1180	for(ml := p.mods; ml != nil; ml = tl ml){
1181		mp := hd ml;
1182		if(mp.total == 0 && mp.totals[0] == 0)
1183			continue;
1184		if((b := getb(mp.path)) == nil)
1185			continue;
1186		sys->print("\nModule: %s(%s)\n\n", mp.name, mp.path);
1187		line := mp.linetab;
1188		if(v&LINE){
1189			bio := bufio->open(b, Bufio->OREAD);
1190			if(bio == nil){
1191				error(sys->sprint("cannot open %s for reading", b));
1192				continue;
1193			}
1194			i = 1;
1195			ll := len line/2;
1196			while((s := bio.gets('\n')) != nil){
1197				f := g := 0;
1198				if(i < ll){
1199					f = line[2*i];
1200					g = line[2*i+1];
1201				}
1202				if(verbose || f != 0 || g != 0){
1203					if(fullhdr)
1204						sys->print("%s:", b);
1205					sys->print("%d\t%d\t%d\t%s", i, f, g, s);
1206				}
1207				i++;
1208			}
1209			if(v&(FUNCTION|MODULE))
1210				sys->print("\n");
1211		}
1212		if(v&FUNCTION){
1213			fun := mp.funtab;
1214			nf := len fun;
1215			for(i = 0; i < nf; i++)
1216				if(verbose || fun[i].count != 0 || fun[i].counte != 0){
1217					if(fullhdr)
1218						sys->print("%s:", b);
1219					sys->print("%d\t%d\t%d\t%s()\n", fun[i].line, fun[i].count, fun[i].counte, fun[i].name);
1220			}
1221			if(v&MODULE)
1222				sys->print("\n");
1223		}
1224		if(v&MODULE)
1225			sys->print("Module totals\t%d\t%d\n\n", mp.total, mp.totals[0]);
1226	}
1227	if(p.mods != nil && tl p.mods != nil)
1228		sys->print("Grand totals\t%d\t%d\n\n", p.total, p.totals[0]);
1229	return 0;
1230}
1231