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
die(char * s)23 die(char *s)
24 {
25 fprint(2, "%s\n", s);
26 exits(s);
27 }
28
29 void
cwrite(int fd,char * path,char * cmd,int len)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 *
newstr(void)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
reader(void * v)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
writer(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
usage(void)157 usage(void)
158 {
159 fprint(2, "Usage: ratrace [-c cmd [arg...]] | [pid]\n");
160 exits("usage");
161 }
162
163 void
hang(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
threadmain(int argc,char ** argv)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