1 #include <u.h> 2 #include <libc.h> 3 #include <bio.h> 4 #include <thread.h> 5 6 enum { 7 Stacksize = 8*1024, 8 Bufsize = 8*1024, 9 }; 10 11 Channel *out; 12 Channel *quit; 13 Channel *forkc; 14 int nread = 0; 15 16 typedef struct Str Str; 17 struct Str { 18 char *buf; 19 int len; 20 }; 21 22 void 23 die(char *s) 24 { 25 fprint(2, "%s\n", s); 26 exits(s); 27 } 28 29 void 30 cwrite(int fd, char *path, char *cmd, int len) 31 { 32 if (write(fd, cmd, len) < len) { 33 fprint(2, "cwrite: %s: failed %d bytes: %r\n", path, len); 34 sendp(quit, nil); 35 threadexits(nil); 36 } 37 } 38 39 void 40 reader(void *v) 41 { 42 int cfd, tfd, forking = 0, pid, newpid; 43 char *ctl, *truss; 44 Str *s; 45 46 pid = (int)(uintptr)v; 47 ctl = smprint("/proc/%d/ctl", pid); 48 if ((cfd = open(ctl, OWRITE)) < 0) 49 die(smprint("%s: %r", ctl)); 50 truss = smprint("/proc/%d/syscall", pid); 51 if ((tfd = open(truss, OREAD)) < 0) 52 die(smprint("%s: %r", truss)); 53 54 cwrite(cfd, ctl, "stop", 4); 55 cwrite(cfd, truss, "startsyscall", 12); 56 57 s = mallocz(sizeof(Str) + Bufsize, 1); 58 s->buf = (char *)&s[1]; 59 while((s->len = pread(tfd, s->buf, Bufsize - 1, 0)) > 0){ 60 if (forking && s->buf[1] == '=' && s->buf[3] != '-') { 61 forking = 0; 62 newpid = strtol(&s->buf[3], 0, 0); 63 sendp(forkc, (void*)newpid); 64 procrfork(reader, (void*)newpid, Stacksize, 0); 65 } 66 67 /* 68 * There are three tests here and they (I hope) guarantee 69 * no false positives. 70 */ 71 if (strstr(s->buf, " Rfork") != nil) { 72 char *a[8]; 73 char *rf; 74 75 rf = strdup(s->buf); 76 if (tokenize(rf, a, 8) == 5) { 77 ulong flags; 78 79 flags = strtoul(a[4], 0, 16); 80 if (flags & RFPROC) 81 forking = 1; 82 } 83 free(rf); 84 } 85 sendp(out, s); 86 cwrite(cfd, truss, "startsyscall", 12); 87 s = mallocz(sizeof(Str) + Bufsize, 1); 88 s->buf = (char *)&s[1]; 89 } 90 sendp(quit, nil); 91 threadexitsall(nil); 92 } 93 94 void 95 writer(void *) 96 { 97 int newpid; 98 Alt a[4]; 99 Str *s; 100 101 a[0].op = CHANRCV; 102 a[0].c = quit; 103 a[0].v = nil; 104 a[1].op = CHANRCV; 105 a[1].c = out; 106 a[1].v = &s; 107 a[2].op = CHANRCV; 108 a[2].c = forkc; 109 a[2].v = &newpid; 110 a[3].op = CHANEND; 111 112 for(;;) 113 switch(alt(a)){ 114 case 0: 115 nread--; 116 if(nread <= 0) 117 goto done; 118 break; 119 case 1: 120 /* it's a nice null terminated thing */ 121 fprint(2, "%s", s->buf); 122 free(s); 123 break; 124 case 2: 125 // procrfork(reader, (void*)newpid, Stacksize, 0); 126 nread++; 127 break; 128 } 129 done: 130 exits(nil); 131 } 132 133 void 134 usage(void) 135 { 136 fprint(2, "Usage: ratrace [-c cmd [arg...]] | [pid]\n"); 137 exits("usage"); 138 } 139 140 void 141 threadmain(int argc, char **argv) 142 { 143 int pid; 144 char *cmd = nil; 145 char **args = nil; 146 147 /* 148 * don't bother with fancy arg processing, because it picks up options 149 * for the command you are starting. Just check for -c as argv[1] 150 * and then take it from there. 151 */ 152 if (argc < 2) 153 usage(); 154 if (argv[1][0] == '-') 155 switch(argv[1][1]) { 156 case 'c': 157 if (argc < 3) 158 usage(); 159 cmd = strdup(argv[2]); 160 args = &argv[2]; 161 break; 162 default: 163 usage(); 164 } 165 166 /* run a command? */ 167 if(cmd) { 168 pid = fork(); 169 if (pid < 0) 170 sysfatal("fork failed: %r"); 171 if(pid == 0) { 172 exec(cmd, args); 173 if(cmd[0] != '/') 174 exec(smprint("/bin/%s", cmd), args); 175 sysfatal("exec %s failed: %r", cmd); 176 } 177 } else { 178 if(argc != 2) 179 usage(); 180 pid = atoi(argv[1]); 181 } 182 183 out = chancreate(sizeof(char*), 0); 184 quit = chancreate(sizeof(char*), 0); 185 forkc = chancreate(sizeof(ulong *), 0); 186 nread++; 187 procrfork(writer, nil, Stacksize, 0); 188 reader((void*)pid); 189 } 190