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