diff options
Diffstat (limited to 'appl/cmd/cprof.b')
| -rw-r--r-- | appl/cmd/cprof.b | 190 |
1 files changed, 190 insertions, 0 deletions
diff --git a/appl/cmd/cprof.b b/appl/cmd/cprof.b new file mode 100644 index 00000000..846a75bd --- /dev/null +++ b/appl/cmd/cprof.b @@ -0,0 +1,190 @@ +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 ... ]"); + 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; +} |
