xref: /freebsd/tools/test/ptrace/scescx.c (revision 38f0b757fd84d17d0fc24739a7cda160c4516d81)
1 /*-
2  * Copyright (c) 2011, 2012 Konstantin Belousov <kib@FreeBSD.org>
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  */
25 
26 #include <sys/cdefs.h>
27 __FBSDID("$FreeBSD$");
28 
29 #include <sys/types.h>
30 #include <sys/ptrace.h>
31 #include <sys/sysctl.h>
32 #include <sys/wait.h>
33 #include <assert.h>
34 #include <errno.h>
35 #include <signal.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <unistd.h>
40 
41 #define TRACE	">>>> "
42 
43 static const char *
44 decode_wait_status(int status)
45 {
46 	static char c[128];
47 	char b[32];
48 	int first;
49 
50 	c[0] = '\0';
51 	first = 1;
52 	if (WIFCONTINUED(status)) {
53 		first = 0;
54 		strlcat(c, "CONT", sizeof(c));
55 	}
56 	if (WIFEXITED(status)) {
57 		if (first)
58 			first = 0;
59 		else
60 			strlcat(c, ",", sizeof(c));
61 		snprintf(b, sizeof(b), "EXIT(%d)", WEXITSTATUS(status));
62 		strlcat(c, b, sizeof(c));
63 	}
64 	if (WIFSIGNALED(status)) {
65 		if (first)
66 			first = 0;
67 		else
68 			strlcat(c, ",", sizeof(c));
69 		snprintf(b, sizeof(b), "SIG(%s)", strsignal(WTERMSIG(status)));
70 		strlcat(c, b, sizeof(c));
71 		if (WCOREDUMP(status))
72 			strlcat(c, ",CORE", sizeof(c));
73 	}
74 	if (WIFSTOPPED(status)) {
75 		if (first)
76 			first = 0;
77 		else
78 			strlcat(c, ",", sizeof(c));
79 		snprintf(b, sizeof(b), "SIG(%s)", strsignal(WSTOPSIG(status)));
80 		strlcat(c, b, sizeof(c));
81 	}
82 	return (c);
83 }
84 
85 static const char *
86 decode_pl_flags(struct ptrace_lwpinfo *lwpinfo)
87 {
88 	static char c[128];
89 	static struct decode_tag {
90 		int flag;
91 		const char *desc;
92 	} decode[] = {
93 		{ PL_FLAG_SA, "SA" },
94 		{ PL_FLAG_BOUND, "BOUND" },
95 		{ PL_FLAG_SCE, "SCE" },
96 		{ PL_FLAG_SCX, "SCX" },
97 		{ PL_FLAG_EXEC, "EXEC" },
98 		{ PL_FLAG_SI, "SI" },
99 		{ PL_FLAG_FORKED, "FORKED" },
100 	};
101 	char de[32];
102 	unsigned first, flags, i;
103 
104 	c[0] = '\0';
105 	first = 1;
106 	flags = lwpinfo->pl_flags;
107 	for (i = 0; i < sizeof(decode) / sizeof(decode[0]); i++) {
108 		if ((flags & decode[i].flag) != 0) {
109 			if (first)
110 				first = 0;
111 			else
112 				strlcat(c, ",", sizeof(c));
113 			strlcat(c, decode[i].desc, sizeof(c));
114 			flags &= ~decode[i].flag;
115 		}
116 	}
117 	for (i = 0; i < sizeof(flags) * NBBY; i++) {
118 		if ((flags & (1 << i)) != 0) {
119 			if (first)
120 				first = 0;
121 			else
122 				strlcat(c, ",", sizeof(c));
123 			snprintf(de, sizeof(de), "<%d>", i);
124 			strlcat(c, de, sizeof(c));
125 		}
126 	}
127 	return (c);
128 }
129 
130 static const char *
131 decode_pl_event(struct ptrace_lwpinfo *lwpinfo)
132 {
133 
134 	switch (lwpinfo->pl_event) {
135 	case PL_EVENT_NONE:
136 		return ("NONE");
137 
138 	case PL_EVENT_SIGNAL:
139 		return ("SIG");
140 
141 	default:
142 		return ("UNKNOWN");
143 	}
144 }
145 
146 static void
147 get_pathname(pid_t pid)
148 {
149 	char pathname[PATH_MAX];
150 	int error, name[4];
151 	size_t len;
152 
153 	name[0] = CTL_KERN;
154 	name[1] = KERN_PROC;
155 	name[2] = KERN_PROC_PATHNAME;
156 	name[3] = pid;
157 
158 	len = sizeof(pathname);
159 	error = sysctl(name, 4, pathname, &len, NULL, 0);
160 	if (error < 0) {
161 		if (errno != ESRCH) {
162 			fprintf(stderr, "sysctl kern.proc.pathname.%d: %s\n",
163 			    pid, strerror(errno));
164 			return;
165 		}
166 		fprintf(stderr, "pid %d exited\n", pid);
167 		return;
168 	}
169 	if (len == 0 || strlen(pathname) == 0) {
170 		fprintf(stderr, "No cached pathname for process %d\n", pid);
171 		return;
172 	}
173 	printf(TRACE "pid %d path %s\n", pid, pathname);
174 }
175 
176 static void
177 wait_info(int pid, int status, struct ptrace_lwpinfo *lwpinfo)
178 {
179 
180 	printf(TRACE "pid %d wait %s", pid,
181 	    decode_wait_status(status));
182 	if (lwpinfo != NULL) {
183 		printf(" event %s flags %s",
184 		    decode_pl_event(lwpinfo), decode_pl_flags(lwpinfo));
185 	}
186 	printf("\n");
187 }
188 
189 static int
190 trace_sc(int pid)
191 {
192 	struct ptrace_lwpinfo lwpinfo;
193 	int status;
194 
195 	if (ptrace(PT_TO_SCE, pid, (caddr_t)1, 0) < 0) {
196 		perror("PT_TO_SCE");
197 		ptrace(PT_KILL, pid, NULL, 0);
198 		return (-1);
199 	}
200 
201 	if (waitpid(pid, &status, 0) == -1) {
202 		perror("waitpid");
203 		return (-1);
204 	}
205 	if (WIFEXITED(status) || WIFSIGNALED(status)) {
206 		wait_info(pid, status, NULL);
207 		return (-1);
208 	}
209 	assert(WIFSTOPPED(status));
210 	assert(WSTOPSIG(status) == SIGTRAP);
211 
212 	if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) {
213 		perror("PT_LWPINFO");
214 		ptrace(PT_KILL, pid, NULL, 0);
215 		return (-1);
216 	}
217 	wait_info(pid, status, &lwpinfo);
218 	assert(lwpinfo.pl_flags & PL_FLAG_SCE);
219 
220 	if (ptrace(PT_TO_SCX, pid, (caddr_t)1, 0) < 0) {
221 		perror("PT_TO_SCX");
222 		ptrace(PT_KILL, pid, NULL, 0);
223 		return (-1);
224 	}
225 
226 	if (waitpid(pid, &status, 0) == -1) {
227 		perror("waitpid");
228 		return (-1);
229 	}
230 	if (WIFEXITED(status) || WIFSIGNALED(status)) {
231 		wait_info(pid, status, NULL);
232 		return (-1);
233 	}
234 	assert(WIFSTOPPED(status));
235 	assert(WSTOPSIG(status) == SIGTRAP);
236 
237 	if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) {
238 		perror("PT_LWPINFO");
239 		ptrace(PT_KILL, pid, NULL, 0);
240 		return (-1);
241 	}
242 	wait_info(pid, status, &lwpinfo);
243 	assert(lwpinfo.pl_flags & PL_FLAG_SCX);
244 
245 	if (lwpinfo.pl_flags & PL_FLAG_EXEC)
246 		get_pathname(pid);
247 
248 	if (lwpinfo.pl_flags & PL_FLAG_FORKED) {
249 		printf(TRACE "forked child %d\n", lwpinfo.pl_child_pid);
250 		return (lwpinfo.pl_child_pid);
251 	}
252 	return (0);
253 }
254 
255 static int
256 trace_cont(int pid)
257 {
258 	struct ptrace_lwpinfo lwpinfo;
259 	int status;
260 
261 	if (ptrace(PT_CONTINUE, pid, (caddr_t)1, 0) < 0) {
262 		perror("PT_CONTINUE");
263 		ptrace(PT_KILL, pid, NULL, 0);
264 		return (-1);
265 	}
266 
267 	if (waitpid(pid, &status, 0) == -1) {
268 		perror("waitpid");
269 		return (-1);
270 	}
271 	if (WIFEXITED(status) || WIFSIGNALED(status)) {
272 		wait_info(pid, status, NULL);
273 		return (-1);
274 	}
275 	assert(WIFSTOPPED(status));
276 	assert(WSTOPSIG(status) == SIGTRAP);
277 
278 	if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) {
279 		perror("PT_LWPINFO");
280 		ptrace(PT_KILL, pid, NULL, 0);
281 		return (-1);
282 	}
283 	wait_info(pid, status, &lwpinfo);
284 
285 	if ((lwpinfo.pl_flags & (PL_FLAG_EXEC | PL_FLAG_SCX)) ==
286 	    (PL_FLAG_EXEC | PL_FLAG_SCX))
287 		get_pathname(pid);
288 
289 	if ((lwpinfo.pl_flags & (PL_FLAG_FORKED | PL_FLAG_SCX)) ==
290 	    (PL_FLAG_FORKED | PL_FLAG_SCX)) {
291 		printf(TRACE "forked child %d\n", lwpinfo.pl_child_pid);
292 		return (lwpinfo.pl_child_pid);
293 	}
294 
295 	return (0);
296 }
297 
298 static int trace_syscalls = 1;
299 
300 static int
301 trace(pid_t pid)
302 {
303 
304 	return (trace_syscalls ? trace_sc(pid) : trace_cont(pid));
305 }
306 
307 
308 int
309 main(int argc, char *argv[])
310 {
311 	struct ptrace_lwpinfo lwpinfo;
312 	int c, status, use_vfork;
313 	pid_t pid, pid1;
314 
315 	trace_syscalls = 1;
316 	use_vfork = 0;
317 	while ((c = getopt(argc, argv, "csv")) != -1) {
318 		switch (c) {
319 		case 'c':
320 			trace_syscalls = 0;
321 			break;
322 		case 's':
323 			trace_syscalls = 1;
324 			break;
325 		case 'v':
326 			use_vfork = 1;
327 			break;
328 		default:
329 		case '?':
330 			fprintf(stderr, "Usage: %s [-c] [-s] [-v]\n", argv[0]);
331 			return (2);
332 		}
333 	}
334 
335 	if ((pid = fork()) < 0) {
336 		perror("fork");
337 		return 1;
338 	}
339 	else if (pid == 0) {
340 		if (ptrace(PT_TRACE_ME, 0, NULL, 0) < 0) {
341 			perror("PT_TRACE_ME");
342 			_exit(1);
343 		}
344 		kill(getpid(), SIGSTOP);
345 		getpid();
346 		if ((pid1 = use_vfork ? vfork() : fork()) < 0) {
347 			perror("fork1");
348 			return (1);
349 		} else if (pid1 == 0) {
350 			printf("Hi from child %d\n", getpid());
351 			execl("/bin/ls", "ls", "/", (char *)NULL);
352 		}
353 	}
354 	else { /* parent */
355 		if (waitpid(pid, &status, 0) == -1) {
356 			perror("waitpid");
357 			return (-1);
358 		}
359 		assert(WIFSTOPPED(status));
360 		assert(WSTOPSIG(status) == SIGSTOP);
361 
362 		if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo,
363 		    sizeof(lwpinfo)) < 0) {
364 			perror("PT_LWPINFO");
365 			ptrace(PT_KILL, pid, NULL, 0);
366 			return (-1);
367 		}
368 		wait_info(pid, status, &lwpinfo);
369 
370 		if (ptrace(PT_FOLLOW_FORK, pid, 0, 1) < 0) {
371 			perror("PT_FOLLOW_FORK");
372 			ptrace(PT_KILL, pid, NULL, 0);
373 			return (2);
374 		}
375 
376 		while ((pid1 = trace(pid)) >= 0) {
377 			if (pid1 != 0) {
378 				printf(TRACE "attached to pid %d\n", pid1);
379 #if 0
380 				kill(pid1, SIGCONT);
381 #endif
382 				if (waitpid(pid1, &status, 0) == -1) {
383 					perror("waitpid");
384 					return (-1);
385 				}
386 				printf(TRACE "nested loop, pid %d status %s\n",
387 				    pid1, decode_wait_status(status));
388 				assert(WIFSTOPPED(status));
389 				assert(WSTOPSIG(status) == SIGSTOP);
390 				if (ptrace(PT_LWPINFO, pid1, (caddr_t)&lwpinfo,
391 				    sizeof(lwpinfo)) < 0) {
392 					perror("PT_LWPINFO");
393 					ptrace(PT_KILL, pid1, NULL, 0);
394 					return (-1);
395 				}
396 				wait_info(pid1, status, &lwpinfo);
397 
398 				while (trace(pid1) >= 0)
399 					;
400 			}
401 		}
402 
403 		ptrace(PT_CONTINUE, pid, (caddr_t)1, 0);
404 	}
405 	return (0);
406 }
407