diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/ratrace.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/ratrace.c')
-rwxr-xr-x | sys/src/cmd/ratrace.c | 189 |
1 files changed, 189 insertions, 0 deletions
diff --git a/sys/src/cmd/ratrace.c b/sys/src/cmd/ratrace.c new file mode 100755 index 000000000..ff5084491 --- /dev/null +++ b/sys/src/cmd/ratrace.c @@ -0,0 +1,189 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <thread.h> + +enum { + Stacksize = 8*1024, + Bufsize = 8*1024, +}; + +Channel *out; +Channel *quit; +Channel *forkc; +int nread = 0; + +typedef struct Str Str; +struct Str { + char *buf; + int len; +}; + +void +die(char *s) +{ + fprint(2, "%s\n", s); + exits(s); +} + +void +cwrite(int fd, char *path, char *cmd, int len) +{ + if (write(fd, cmd, len) < len) { + fprint(2, "cwrite: %s: failed %d bytes: %r\n", path, len); + sendp(quit, nil); + threadexits(nil); + } +} + +void +reader(void *v) +{ + int cfd, tfd, forking = 0, pid, newpid; + char *ctl, *truss; + Str *s; + + pid = (int)(uintptr)v; + ctl = smprint("/proc/%d/ctl", pid); + if ((cfd = open(ctl, OWRITE)) < 0) + die(smprint("%s: %r", ctl)); + truss = smprint("/proc/%d/syscall", pid); + if ((tfd = open(truss, OREAD)) < 0) + die(smprint("%s: %r", truss)); + + cwrite(cfd, ctl, "stop", 4); + cwrite(cfd, truss, "startsyscall", 12); + + s = mallocz(sizeof(Str) + Bufsize, 1); + s->buf = (char *)&s[1]; + while((s->len = pread(tfd, s->buf, Bufsize - 1, 0)) > 0){ + if (forking && s->buf[1] == '=' && s->buf[3] != '-') { + forking = 0; + newpid = strtol(&s->buf[3], 0, 0); + sendp(forkc, (void*)newpid); + procrfork(reader, (void*)newpid, Stacksize, 0); + } + + /* + * There are three tests here and they (I hope) guarantee + * no false positives. + */ + if (strstr(s->buf, " Rfork") != nil) { + char *a[8]; + char *rf; + + rf = strdup(s->buf); + if (tokenize(rf, a, 8) == 5) { + ulong flags; + + flags = strtoul(a[4], 0, 16); + if (flags & RFPROC) + forking = 1; + } + free(rf); + } + sendp(out, s); + cwrite(cfd, truss, "startsyscall", 12); + s = mallocz(sizeof(Str) + Bufsize, 1); + s->buf = (char *)&s[1]; + } + sendp(quit, nil); + threadexitsall(nil); +} + +void +writer(void *) +{ + int newpid; + Alt a[4]; + Str *s; + + a[0].op = CHANRCV; + a[0].c = quit; + a[0].v = nil; + a[1].op = CHANRCV; + a[1].c = out; + a[1].v = &s; + a[2].op = CHANRCV; + a[2].c = forkc; + a[2].v = &newpid; + a[3].op = CHANEND; + + for(;;) + switch(alt(a)){ + case 0: + nread--; + if(nread <= 0) + goto done; + break; + case 1: + /* it's a nice null terminated thing */ + fprint(2, "%s", s->buf); + free(s); + break; + case 2: + // procrfork(reader, (void*)newpid, Stacksize, 0); + nread++; + break; + } +done: + exits(nil); +} + +void +usage(void) +{ + fprint(2, "Usage: ratrace [-c cmd [arg...]] | [pid]\n"); + exits("usage"); +} + +void +threadmain(int argc, char **argv) +{ + int pid; + char *cmd = nil; + char **args = nil; + + /* + * don't bother with fancy arg processing, because it picks up options + * for the command you are starting. Just check for -c as argv[1] + * and then take it from there. + */ + if (argc < 2) + usage(); + if (argv[1][0] == '-') + switch(argv[1][1]) { + case 'c': + if (argc < 3) + usage(); + cmd = strdup(argv[2]); + args = &argv[2]; + break; + default: + usage(); + } + + /* run a command? */ + if(cmd) { + pid = fork(); + if (pid < 0) + sysfatal("fork failed: %r"); + if(pid == 0) { + exec(cmd, args); + if(cmd[0] != '/') + exec(smprint("/bin/%s", cmd), args); + sysfatal("exec %s failed: %r", cmd); + } + } else { + if(argc != 2) + usage(); + pid = atoi(argv[1]); + } + + out = chancreate(sizeof(char*), 0); + quit = chancreate(sizeof(char*), 0); + forkc = chancreate(sizeof(ulong *), 0); + nread++; + procrfork(writer, nil, Stacksize, 0); + reader((void*)pid); +} |