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 werrstr(""); 33 if (write(fd, cmd, len) < len) { 34 fprint(2, "cwrite: %s: failed writing %d bytes: %r\n", 35 path, len); 36 sendp(quit, nil); 37 threadexits(nil); 38 } 39 } 40 41 Str * 42 newstr(void) 43 { 44 Str *s; 45 46 s = mallocz(sizeof(Str) + Bufsize, 1); 47 if (s == nil) 48 sysfatal("malloc"); 49 s->buf = (char *)&s[1]; 50 return s; 51 } 52 53 void 54 reader(void *v) 55 { 56 int cfd, tfd, forking = 0, exiting, pid, newpid; 57 char *ctl, *truss; 58 Str *s; 59 static char start[] = "start"; 60 static char waitstop[] = "waitstop"; 61 62 pid = (int)(uintptr)v; 63 ctl = smprint("/proc/%d/ctl", pid); 64 if ((cfd = open(ctl, OWRITE)) < 0) 65 die(smprint("%s: %r", ctl)); 66 truss = smprint("/proc/%d/syscall", pid); 67 if ((tfd = open(truss, OREAD)) < 0) 68 die(smprint("%s: %r", truss)); 69 70 /* child was stopped by hang msg earlier */ 71 cwrite(cfd, ctl, waitstop, sizeof waitstop - 1); 72 73 cwrite(cfd, ctl, "startsyscall", 12); 74 s = newstr(); 75 exiting = 0; 76 while((s->len = pread(tfd, s->buf, Bufsize - 1, 0)) >= 0){ 77 if (forking && s->buf[1] == '=' && s->buf[3] != '-') { 78 forking = 0; 79 newpid = strtol(&s->buf[3], 0, 0); 80 sendp(forkc, (void*)newpid); 81 procrfork(reader, (void*)newpid, Stacksize, 0); 82 } 83 84 /* 85 * There are three tests here and they (I hope) guarantee 86 * no false positives. 87 */ 88 if (strstr(s->buf, " Rfork") != nil) { 89 char *a[8]; 90 char *rf; 91 92 rf = strdup(s->buf); 93 if (tokenize(rf, a, 8) == 5 && 94 strtoul(a[4], 0, 16) & RFPROC) 95 forking = 1; 96 free(rf); 97 } else if (strstr(s->buf, " Exits") != nil) 98 exiting = 1; 99 100 sendp(out, s); /* print line from /proc/$child/syscall */ 101 if (exiting) { 102 s = newstr(); 103 strcpy(s->buf, "\n"); 104 sendp(out, s); 105 break; 106 } 107 108 /* flush syscall trace buffer */ 109 cwrite(cfd, ctl, "startsyscall", 12); 110 s = newstr(); 111 } 112 113 sendp(quit, nil); 114 threadexitsall(nil); 115 } 116 117 void 118 writer(void *) 119 { 120 int newpid; 121 Alt a[4]; 122 Str *s; 123 124 a[0].op = CHANRCV; 125 a[0].c = quit; 126 a[0].v = nil; 127 a[1].op = CHANRCV; 128 a[1].c = out; 129 a[1].v = &s; 130 a[2].op = CHANRCV; 131 a[2].c = forkc; 132 a[2].v = &newpid; 133 a[3].op = CHANEND; 134 135 for(;;) 136 switch(alt(a)){ 137 case 0: /* quit */ 138 nread--; 139 if(nread <= 0) 140 goto done; 141 break; 142 case 1: /* out */ 143 /* it's a nice null terminated thing */ 144 fprint(2, "%s", s->buf); 145 free(s); 146 break; 147 case 2: /* forkc */ 148 // procrfork(reader, (void*)newpid, Stacksize, 0); 149 nread++; 150 break; 151 } 152 done: 153 exits(nil); 154 } 155 156 void 157 usage(void) 158 { 159 fprint(2, "Usage: ratrace [-c cmd [arg...]] | [pid]\n"); 160 exits("usage"); 161 } 162 163 void 164 hang(void) 165 { 166 int me; 167 char *myctl; 168 static char hang[] = "hang"; 169 170 myctl = smprint("/proc/%d/ctl", getpid()); 171 me = open(myctl, OWRITE); 172 if (me < 0) 173 sysfatal("can't open %s: %r", myctl); 174 cwrite(me, myctl, hang, sizeof hang - 1); 175 close(me); 176 free(myctl); 177 } 178 179 void 180 threadmain(int argc, char **argv) 181 { 182 int pid; 183 char *cmd = nil; 184 char **args = nil; 185 186 /* 187 * don't bother with fancy arg processing, because it picks up options 188 * for the command you are starting. Just check for -c as argv[1] 189 * and then take it from there. 190 */ 191 if (argc < 2) 192 usage(); 193 while (argv[1][0] == '-') { 194 switch(argv[1][1]) { 195 case 'c': 196 if (argc < 3) 197 usage(); 198 cmd = strdup(argv[2]); 199 args = &argv[2]; 200 break; 201 default: 202 usage(); 203 } 204 ++argv; 205 --argc; 206 } 207 208 /* run a command? */ 209 if(cmd) { 210 pid = fork(); 211 if (pid < 0) 212 sysfatal("fork failed: %r"); 213 if(pid == 0) { 214 hang(); 215 exec(cmd, args); 216 if(cmd[0] != '/') 217 exec(smprint("/bin/%s", cmd), args); 218 sysfatal("exec %s failed: %r", cmd); 219 } 220 } else { 221 if(argc != 2) 222 usage(); 223 pid = atoi(argv[1]); 224 } 225 226 out = chancreate(sizeof(char*), 0); 227 quit = chancreate(sizeof(char*), 0); 228 forkc = chancreate(sizeof(ulong *), 0); 229 nread++; 230 procrfork(writer, nil, Stacksize, 0); 231 reader((void*)pid); 232 } 233