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