xref: /inferno-os/appl/cmd/mprof.b (revision a445bc484ec282bd86818febcadf7ee102d450f0)
1implement Prof;
2
3include "sys.m";
4	sys: Sys;
5include "draw.m";
6include "arg.m";
7	arg: Arg;
8include "profile.m";
9	profile: Profile;
10include "sh.m";
11
12stderr: ref Sys->FD;
13
14Prof: module {
15	init: fn(nil: ref Draw->Context, argv: list of string);
16	init0: fn(nil: ref Draw->Context, argv: list of string): Profile->Prof;
17};
18
19ignored(s: string)
20{
21	sys->fprint(stderr, "mprof: warning: %s ignored\n", s);
22}
23
24exits(e: string)
25{
26	if(profile != nil)
27		profile->end();
28	raise "fail:" + e;
29}
30
31pfatal(s: string)
32{
33	sys->fprint(stderr, "mprof: %s: %s\n", s, profile->lasterror());
34	exits("error");
35}
36
37badmodule(p: string)
38{
39	sys->fprint(stderr, "mprof: cannot load %s: %r\n", p);
40	exits("bad module");
41}
42
43usage(s: string)
44{
45	sys->fprint(stderr, "mprof: %s\n", s);
46	sys->fprint(stderr, "usage: mprof [-bcMflnve] [-m modname]... [cmd arg ...]\n");
47	exits("usage");
48}
49
50init(ctxt: ref Draw->Context, argv: list of string)
51{
52	init0(ctxt, argv);
53}
54
55init0(ctxt: ref Draw->Context, argv: list of string): Profile->Prof
56{
57	sys = load Sys Sys->PATH;
58	stderr = sys->fildes(2);
59	arg = load Arg Arg->PATH;
60	if(arg == nil)
61		badmodule(Arg->PATH);
62	arg->init(argv);
63	profile = load Profile Profile->PATH;
64	if(profile == nil)
65		badmodule(Profile->PATH);
66	if(profile->init() < 0)
67		pfatal("cannot initialize profile device");
68
69	v := 0;
70	begin := end := 0;
71	ep := 0;
72	wm := 0;
73	mem := 0;
74	exec, mods: list of string;
75	while((c := arg->opt()) != 0){
76		case c {
77			'b' => begin = 1;
78			'c' => end = 1;
79			'M' => v |= profile->MODULE;
80			'f' => v |= profile->FUNCTION;
81			'l' => v |= profile->LINE;
82			'n' => v |= profile->FULLHDR;
83			'v' => v |= profile->VERBOSE;
84			'm' =>
85				if((s := arg->arg()) == nil)
86					usage("missing module name");
87				mods = s :: mods;
88			'e' =>
89				ep = 1;
90			'g' =>
91				wm = 1;
92			'1' =>
93				mem |= Profile->MAIN;
94			'2' =>
95				mem |= Profile->HEAP;
96			'3' =>
97				mem |= Profile->IMAGE;
98			* =>
99				usage(sys->sprint("unknown option -%c", c));
100		}
101	}
102
103	exec = arg->argv();
104
105	if(begin && end)
106		ignored("-e option");
107	if((begin || end) && v != 0)
108		ignored("output format");
109	if(begin && exec != nil)
110		begin = 0;
111	if(begin == 0 && exec == nil){
112		if(mods != nil)
113			ignored("-m option");
114		mods = nil;
115	}
116	if(end){
117		if(mods != nil)
118			ignored("-m option");
119		if(ep || exec != nil)
120			ignored("command");
121		profile->end();
122		exit;
123	}
124
125	for( ; mods != nil; mods = tl mods)
126		profile->profile(hd mods);
127
128	if(begin){
129		if(profile->memstart(mem) < 0)
130			pfatal("cannot start profiling");
131		exit;
132	}
133	r := 0;
134	if(exec != nil){
135		if(ep)
136			profile->profile(disname(hd exec));
137		if(profile->memstart(mem) < 0)
138			pfatal("cannot start profiling");
139		# r = run(ctxt, hd exec, exec);
140		wfd := openwait(sys->pctl(0, nil));
141		ci := chan of int;
142		spawn execute(ctxt, hd exec, exec, ci);
143		epid := <- ci;
144		wait(wfd, epid);
145	}
146	if(profile->stop() < 0)
147		pfatal("cannot stop profiling");
148	if(exec == nil || r >= 0){
149		modl := profile->memstats();
150		if(modl.mods == nil)
151			pfatal("no profile information");
152		if(wm){
153			if(exec == nil){
154				if(profile->memstart(mem) < 0)
155					pfatal("cannot restart profiling");
156			}
157			else
158				profile->end();
159			return modl;
160		}
161		if(!(v&(profile->MODULE|profile->FUNCTION|profile->LINE)))
162			v |= profile->MODULE|profile->LINE;
163		if(profile->memshow(modl, v) < 0)
164			pfatal("cannot show profile");
165		if(exec == nil){
166			if(profile->memstart(mem) < 0)
167				pfatal("cannot restart profiling");
168			exit;
169		}
170	}
171	profile->end();
172	return (nil, 0, nil);
173}
174
175disname(cmd: string): string
176{
177	file := cmd;
178	if(len file<4 || file[len file-4:]!=".dis")
179		file += ".dis";
180	if(exists(file))
181		return file;
182	if(file[0]!='/' && file[0:2]!="./")
183		file = "/dis/"+file;
184	# if(exists(file))
185	#	return file;
186	return file;
187}
188
189execute(ctxt: ref Draw->Context, cmd : string, argl : list of string, ci: chan of int)
190{
191	ci <-= sys->pctl(Sys->FORKNS|Sys->NEWFD|Sys->NEWPGRP, 0 :: 1 :: 2 :: stderr.fd :: nil);
192	file := cmd;
193	if(len file<4 || file[len file-4:]!=".dis")
194		file += ".dis";
195	c := load Command file;
196	if(c == nil) {
197		err := sys->sprint("%r");
198		if(file[0]!='/' && file[0:2]!="./"){
199			c = load Command "/dis/"+file;
200			if(c == nil)
201				err = sys->sprint("%r");
202		}
203		if(c == nil){
204			sys->fprint(stderr, "mprof: %s: %s\n", cmd, err);
205			return;
206		}
207	}
208	c->init(ctxt, argl);
209}
210
211# run(ctxt: ref Draw->Context, cmd : string, argl : list of string): int
212# {
213# 	file := cmd;
214# 	if(len file<4 || file[len file-4:]!=".dis")
215# 		file += ".dis";
216# 	c := load Command file;
217# 	if(c == nil) {
218# 		err := sys->sprint("%r");
219# 		if(file[0]!='/' && file[0:2]!="./"){
220# 			c = load Command "/dis/"+file;
221# 			if(c == nil)
222# 				err = sys->sprint("%r");
223# 		}
224# 		if(c == nil){
225# 			sys->fprint(stderr, "mprof: %s: %s\n", cmd, err);
226# 			return -1;
227# 		}
228# 	}
229# 	c->init(ctxt, argl);
230# 	return 0;
231# }
232
233openwait(pid : int) : ref Sys->FD
234{
235	w := sys->sprint("#p/%d/wait", pid);
236	fd := sys->open(w, Sys->OREAD);
237	if (fd == nil)
238		pfatal("fd == nil in wait");
239	return fd;
240}
241
242wait(wfd : ref Sys->FD, wpid : int)
243{
244	n : int;
245
246	buf := array[Sys->WAITLEN] of byte;
247	status := "";
248	for(;;) {
249		if ((n = sys->read(wfd, buf, len buf)) < 0)
250			pfatal("bad read in wait");
251		status = string buf[0:n];
252		if (int status == wpid)
253			break;
254	}
255}
256
257exists(f: string): int
258{
259	return sys->open(f, Sys->OREAD) != nil;
260}
261