xref: /inferno-os/appl/cmd/cprof.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->Coverage;
17};
18
19exits(e: string)
20{
21	if(profile != nil)
22		profile->end();
23	raise "fail:" + e;
24}
25
26pfatal(s: string)
27{
28	sys->fprint(stderr, "cprof: %s: %s\n", s, profile->lasterror());
29	exits("error");
30}
31
32badmodule(p: string)
33{
34	sys->fprint(stderr, "cprof: cannot load %s: %r\n", p);
35	exits("bad module");
36}
37
38usage(s: string)
39{
40	sys->fprint(stderr, "cprof: %s\n", s);
41	sys->fprint(stderr, "usage: cprof [-fner] [-m modname]... cmd [arg ... ]\n");
42	exits("usage");
43}
44
45init(ctxt: ref Draw->Context, argv: list of string)
46{
47	init0(ctxt, argv);
48}
49
50init0(ctxt: ref Draw->Context, argv: list of string): Profile->Coverage
51{
52	sys = load Sys Sys->PATH;
53	stderr = sys->fildes(2);
54	arg = load Arg Arg->PATH;
55	if(arg == nil)
56		badmodule(Arg->PATH);
57	arg->init(argv);
58	profile = load Profile Profile->PATH;
59	if(profile == nil)
60		badmodule(Profile->PATH);
61	if(profile->init() < 0)
62		pfatal("cannot initialize profile device");
63
64	v := 0;
65	ep := 0;
66	rec := 0;
67	wm := 0;
68	exec, mods: list of string;
69	while((c := arg->opt()) != 0){
70		case c {
71			'n' => v |= profile->FULLHDR;
72			'f' => v |= profile->FREQUENCY;
73			'm' =>
74				if((s := arg->arg()) == nil)
75					usage("missing module/file");
76				mods = s :: mods;
77			'e' =>
78				ep = 1;
79			'r' =>
80				rec = 1;
81			'g' =>
82				wm = 1;
83			* =>
84				usage(sys->sprint("unknown option -%c", c));
85		}
86	}
87	exec = arg->argv();
88	# if(exec == nil)
89	#	usage("nothing to execute");
90	for( ; mods != nil; mods = tl mods)
91		profile->profile(hd mods);
92	if(ep && exec != nil)
93		profile->profile(disname(hd exec));
94	if(exec != nil){
95		wfd := openwait(sys->pctl(0, nil));
96		ci := chan of int;
97		spawn execute(ctxt, hd exec, exec, ci);
98		epid := <- ci;
99		if(profile->cpstart(epid) < 0){
100			ci <-= 0;
101			pfatal("cannot start profiling");
102		}
103		ci <-= 1;
104		wait(wfd, epid);
105		if(profile->stop() < 0)
106			pfatal("cannot stop profiling");
107	}
108	if(exec == nil)
109		modl := profile->cpfstats(v);
110	else
111		modl = profile->cpstats(rec, v);
112	if(modl.mods == nil)
113		pfatal("no profile information");
114	if(wm){
115		cvr := profile->coverage(modl, v);
116		profile->end();
117		return cvr;
118	}
119	if(!rec && profile->cpshow(modl, v) < 0)
120		pfatal("cannot show profile");
121	profile->end();
122	return nil;
123}
124
125disname(cmd: string): string
126{
127	file := cmd;
128	if(len file<4 || file[len file-4:]!=".dis")
129		file += ".dis";
130	if(exists(file))
131		return file;
132	if(file[0]!='/' && file[0:2]!="./")
133		file = "/dis/"+file;
134	# if(exists(file))
135	#	return file;
136	return file;
137}
138
139execute(ctxt: ref Draw->Context, cmd : string, argl : list of string, ci: chan of int)
140{
141	ci <-= sys->pctl(Sys->FORKNS|Sys->NEWFD|Sys->NEWPGRP, 0 :: 1 :: 2 :: stderr.fd :: nil);
142	file := cmd;
143	err := "";
144	if(len file<4 || file[len file-4:]!=".dis")
145		file += ".dis";
146	c := load Command file;
147	if(c == nil) {
148		err = sys->sprint("%r");
149		if(file[0]!='/' && file[0:2]!="./"){
150			c = load Command "/dis/"+file;
151			if(c == nil)
152				err = sys->sprint("%r");
153		}
154	}
155	if(<- ci){
156		if(c == nil)
157			sys->fprint(stderr, "cprof: %s: %s\n", cmd, err);
158		else
159			c->init(ctxt, argl);
160	}
161}
162
163openwait(pid : int) : ref Sys->FD
164{
165	w := sys->sprint("#p/%d/wait", pid);
166	fd := sys->open(w, Sys->OREAD);
167	if (fd == nil)
168		pfatal("fd == nil in wait");
169	return fd;
170}
171
172wait(wfd : ref Sys->FD, wpid : int)
173{
174	n : int;
175
176	buf := array[Sys->WAITLEN] of byte;
177	status := "";
178	for(;;) {
179		if ((n = sys->read(wfd, buf, len buf)) < 0)
180			pfatal("bad read in wait");
181		status = string buf[0:n];
182		if (int status == wpid)
183			break;
184	}
185}
186
187exists(f: string): int
188{
189	return sys->open(f, Sys->OREAD) != nil;
190}
191