xref: /plan9/sys/src/cmd/ratrace.c (revision aaf0c99d0234073f3ddc814e3b2ce1542f14fa95)
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