shithub: purgatorio

ref: 3efb5bbb4061056e523858b134c555949591efe2
dir: /appl/cmd/cprof.b/

View raw version
implement Prof;

include "sys.m";
	sys: Sys;
include "draw.m";
include "arg.m";
	arg: Arg;
include "profile.m";
	profile: Profile;
include "sh.m";

stderr: ref Sys->FD;

Prof: module {
	init: fn(nil: ref Draw->Context, argv: list of string);
	init0: fn(nil: ref Draw->Context, argv: list of string): Profile->Coverage;
};

exits(e: string)
{
	if(profile != nil)
		profile->end();
	raise "fail:" + e;
}

pfatal(s: string)
{
	sys->fprint(stderr, "cprof: %s: %s\n", s, profile->lasterror());
	exits("error");
}

badmodule(p: string)
{
	sys->fprint(stderr, "cprof: cannot load %s: %r\n", p);
	exits("bad module");
}

usage(s: string)
{
	sys->fprint(stderr, "cprof: %s\n", s);
	sys->fprint(stderr, "usage: cprof [-fner] [-m modname]... cmd [arg ... ]\n");
	exits("usage");
}

init(ctxt: ref Draw->Context, argv: list of string)
{
	init0(ctxt, argv);
}

init0(ctxt: ref Draw->Context, argv: list of string): Profile->Coverage
{
	sys = load Sys Sys->PATH;
	stderr = sys->fildes(2);
	arg = load Arg Arg->PATH;
	if(arg == nil)
		badmodule(Arg->PATH);
	arg->init(argv);
	profile = load Profile Profile->PATH;
	if(profile == nil)
		badmodule(Profile->PATH);
	if(profile->init() < 0)
		pfatal("cannot initialize profile device");

	v := 0;
	ep := 0;
	rec := 0;
	wm := 0;
	exec, mods: list of string;
	while((c := arg->opt()) != 0){
		case c {
			'n' => v |= profile->FULLHDR;
			'f' => v |= profile->FREQUENCY;
			'm' =>
				if((s := arg->arg()) == nil)
					usage("missing module/file");
				mods = s :: mods;
			'e' =>
				ep = 1;
			'r' =>
				rec = 1;
			'g' =>
				wm = 1;
			* => 
				usage(sys->sprint("unknown option -%c", c));
		}
	}
	exec = arg->argv();
	# if(exec == nil)
	#	usage("nothing to execute");
	for( ; mods != nil; mods = tl mods)
		profile->profile(hd mods);
	if(ep && exec != nil)
		profile->profile(disname(hd exec));
	if(exec != nil){
		wfd := openwait(sys->pctl(0, nil));
		ci := chan of int;
		spawn execute(ctxt, hd exec, exec, ci);
		epid := <- ci;
		if(profile->cpstart(epid) < 0){
			ci <-= 0;
			pfatal("cannot start profiling");
		}
		ci <-= 1;
		wait(wfd, epid);
		if(profile->stop() < 0)
			pfatal("cannot stop profiling");
	}
	if(exec == nil)
		modl := profile->cpfstats(v);
	else
		modl = profile->cpstats(rec, v);
	if(modl.mods == nil)
		pfatal("no profile information");
	if(wm){
		cvr := profile->coverage(modl, v);
		profile->end();
		return cvr;
	}
	if(!rec && profile->cpshow(modl, v) < 0)
		pfatal("cannot show profile");
	profile->end();
	return nil;
}

disname(cmd: string): string
{
	file := cmd;
	if(len file<4 || file[len file-4:]!=".dis")
		file += ".dis";
	if(exists(file))
		return file;
	if(file[0]!='/' && file[0:2]!="./")
		file = "/dis/"+file;
	# if(exists(file))
	#	return file;
	return file;
}

execute(ctxt: ref Draw->Context, cmd : string, argl : list of string, ci: chan of int)
{
	ci <-= sys->pctl(Sys->FORKNS|Sys->NEWFD|Sys->NEWPGRP, 0 :: 1 :: 2 :: stderr.fd :: nil);
	file := cmd;
	err := "";
	if(len file<4 || file[len file-4:]!=".dis")
		file += ".dis";
	c := load Command file;
	if(c == nil) {
		err = sys->sprint("%r");
		if(file[0]!='/' && file[0:2]!="./"){
			c = load Command "/dis/"+file;
			if(c == nil)
				err = sys->sprint("%r");
		}
	}
	if(<- ci){
		if(c == nil)
			sys->fprint(stderr, "cprof: %s: %s\n", cmd, err);
		else
			c->init(ctxt, argl);
	}
}

openwait(pid : int) : ref Sys->FD
{
	w := sys->sprint("#p/%d/wait", pid);
	fd := sys->open(w, Sys->OREAD);
	if (fd == nil)
		pfatal("fd == nil in wait");
	return fd;
}

wait(wfd : ref Sys->FD, wpid : int)
{
	n : int;

	buf := array[Sys->WAITLEN] of byte;
	status := "";
	for(;;) {
		if ((n = sys->read(wfd, buf, len buf)) < 0)
			pfatal("bad read in wait");
		status = string buf[0:n];
		if (int status == wpid)
			break;
	}
}

exists(f: string): int
{
	return sys->open(f, Sys->OREAD) != nil;
}