xref: /freebsd/tools/test/ptrace/scescx.c (revision b00ab754)
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 		{ PL_FLAG_CHILD, "CHILD" },
101 		{ PL_FLAG_BORN, "LWPBORN" },
102 		{ PL_FLAG_EXITED, "LWPEXITED" },
103 		{ PL_FLAG_VFORKED, "VFORKED" },
104 		{ PL_FLAG_VFORK_DONE, "VFORKDONE" },
105 	};
106 	char de[32];
107 	unsigned first, flags, i;
108 
109 	c[0] = '\0';
110 	first = 1;
111 	flags = lwpinfo->pl_flags;
112 	for (i = 0; i < sizeof(decode) / sizeof(decode[0]); i++) {
113 		if ((flags & decode[i].flag) != 0) {
114 			if (first)
115 				first = 0;
116 			else
117 				strlcat(c, ",", sizeof(c));
118 			strlcat(c, decode[i].desc, sizeof(c));
119 			flags &= ~decode[i].flag;
120 		}
121 	}
122 	for (i = 0; i < sizeof(flags) * NBBY; i++) {
123 		if ((flags & (1 << i)) != 0) {
124 			if (first)
125 				first = 0;
126 			else
127 				strlcat(c, ",", sizeof(c));
128 			snprintf(de, sizeof(de), "<%d>", i);
129 			strlcat(c, de, sizeof(c));
130 		}
131 	}
132 	return (c);
133 }
134 
135 static const char *
136 decode_pl_event(struct ptrace_lwpinfo *lwpinfo)
137 {
138 
139 	switch (lwpinfo->pl_event) {
140 	case PL_EVENT_NONE:
141 		return ("NONE");
142 
143 	case PL_EVENT_SIGNAL:
144 		return ("SIG");
145 
146 	default:
147 		return ("UNKNOWN");
148 	}
149 }
150 
151 static void
152 get_pathname(pid_t pid)
153 {
154 	char pathname[PATH_MAX];
155 	int error, name[4];
156 	size_t len;
157 
158 	name[0] = CTL_KERN;
159 	name[1] = KERN_PROC;
160 	name[2] = KERN_PROC_PATHNAME;
161 	name[3] = pid;
162 
163 	len = sizeof(pathname);
164 	error = sysctl(name, 4, pathname, &len, NULL, 0);
165 	if (error < 0) {
166 		if (errno != ESRCH) {
167 			fprintf(stderr, "sysctl kern.proc.pathname.%d: %s\n",
168 			    pid, strerror(errno));
169 			return;
170 		}
171 		fprintf(stderr, "pid %d exited\n", pid);
172 		return;
173 	}
174 	if (len == 0 || strlen(pathname) == 0) {
175 		fprintf(stderr, "No cached pathname for process %d\n", pid);
176 		return;
177 	}
178 	printf(TRACE "pid %d path %s\n", pid, pathname);
179 }
180 
181 static void
182 wait_info(int pid, int status, struct ptrace_lwpinfo *lwpinfo)
183 {
184 	long *args;
185 	int error, i;
186 
187 	printf(TRACE "pid %d wait %s", pid,
188 	    decode_wait_status(status));
189 	if (lwpinfo != NULL) {
190 		printf(" event %s flags %s",
191 		    decode_pl_event(lwpinfo), decode_pl_flags(lwpinfo));
192 		if ((lwpinfo->pl_flags & (PL_FLAG_SCE | PL_FLAG_SCX)) != 0) {
193 			printf(" sc%d", lwpinfo->pl_syscall_code);
194 			args = calloc(lwpinfo->pl_syscall_narg, sizeof(long));
195 			error = ptrace(PT_GET_SC_ARGS, lwpinfo->pl_lwpid,
196 			    (caddr_t)args, lwpinfo->pl_syscall_narg *
197 			    sizeof(long));
198 			if (error == 0) {
199 				for (i = 0; i < (int)lwpinfo->pl_syscall_narg;
200 				    i++) {
201 					printf("%c%#lx", i == 0 ? '(' : ',',
202 					    args[i]);
203 				}
204 			} else {
205 				fprintf(stderr, "PT_GET_SC_ARGS failed: %s",
206 				    strerror(errno));
207 			}
208 			printf(")");
209 			free(args);
210 		}
211 	}
212 	printf("\n");
213 }
214 
215 static int
216 trace_sc(int pid)
217 {
218 	struct ptrace_lwpinfo lwpinfo;
219 	int status;
220 
221 	if (ptrace(PT_TO_SCE, pid, (caddr_t)1, 0) < 0) {
222 		perror("PT_TO_SCE");
223 		ptrace(PT_KILL, pid, NULL, 0);
224 		return (-1);
225 	}
226 
227 	if (waitpid(pid, &status, 0) == -1) {
228 		perror("waitpid");
229 		return (-1);
230 	}
231 	if (WIFEXITED(status) || WIFSIGNALED(status)) {
232 		wait_info(pid, status, NULL);
233 		return (-1);
234 	}
235 	assert(WIFSTOPPED(status));
236 	assert(WSTOPSIG(status) == SIGTRAP);
237 
238 	if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) {
239 		perror("PT_LWPINFO");
240 		ptrace(PT_KILL, pid, NULL, 0);
241 		return (-1);
242 	}
243 	wait_info(pid, status, &lwpinfo);
244 	assert(lwpinfo.pl_flags & PL_FLAG_SCE);
245 
246 	if (ptrace(PT_TO_SCX, pid, (caddr_t)1, 0) < 0) {
247 		perror("PT_TO_SCX");
248 		ptrace(PT_KILL, pid, NULL, 0);
249 		return (-1);
250 	}
251 
252 	if (waitpid(pid, &status, 0) == -1) {
253 		perror("waitpid");
254 		return (-1);
255 	}
256 	if (WIFEXITED(status) || WIFSIGNALED(status)) {
257 		wait_info(pid, status, NULL);
258 		return (-1);
259 	}
260 	assert(WIFSTOPPED(status));
261 	assert(WSTOPSIG(status) == SIGTRAP);
262 
263 	if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) {
264 		perror("PT_LWPINFO");
265 		ptrace(PT_KILL, pid, NULL, 0);
266 		return (-1);
267 	}
268 	wait_info(pid, status, &lwpinfo);
269 	assert(lwpinfo.pl_flags & PL_FLAG_SCX);
270 
271 	if (lwpinfo.pl_flags & PL_FLAG_EXEC)
272 		get_pathname(pid);
273 
274 	if (lwpinfo.pl_flags & PL_FLAG_FORKED) {
275 		printf(TRACE "forked child %d\n", lwpinfo.pl_child_pid);
276 		return (lwpinfo.pl_child_pid);
277 	}
278 	return (0);
279 }
280 
281 static int
282 trace_cont(int pid)
283 {
284 	struct ptrace_lwpinfo lwpinfo;
285 	int status;
286 
287 	if (ptrace(PT_CONTINUE, pid, (caddr_t)1, 0) < 0) {
288 		perror("PT_CONTINUE");
289 		ptrace(PT_KILL, pid, NULL, 0);
290 		return (-1);
291 	}
292 
293 	if (waitpid(pid, &status, 0) == -1) {
294 		perror("waitpid");
295 		return (-1);
296 	}
297 	if (WIFEXITED(status) || WIFSIGNALED(status)) {
298 		wait_info(pid, status, NULL);
299 		return (-1);
300 	}
301 	assert(WIFSTOPPED(status));
302 	assert(WSTOPSIG(status) == SIGTRAP);
303 
304 	if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) {
305 		perror("PT_LWPINFO");
306 		ptrace(PT_KILL, pid, NULL, 0);
307 		return (-1);
308 	}
309 	wait_info(pid, status, &lwpinfo);
310 
311 	if ((lwpinfo.pl_flags & (PL_FLAG_EXEC | PL_FLAG_SCX)) ==
312 	    (PL_FLAG_EXEC | PL_FLAG_SCX))
313 		get_pathname(pid);
314 
315 	if ((lwpinfo.pl_flags & (PL_FLAG_FORKED | PL_FLAG_SCX)) ==
316 	    (PL_FLAG_FORKED | PL_FLAG_SCX)) {
317 		printf(TRACE "forked child %d\n", lwpinfo.pl_child_pid);
318 		return (lwpinfo.pl_child_pid);
319 	}
320 
321 	return (0);
322 }
323 
324 static int trace_syscalls = 1;
325 
326 static int
327 trace(pid_t pid)
328 {
329 
330 	return (trace_syscalls ? trace_sc(pid) : trace_cont(pid));
331 }
332 
333 
334 int
335 main(int argc, char *argv[])
336 {
337 	struct ptrace_lwpinfo lwpinfo;
338 	int c, status, use_vfork;
339 	pid_t pid, pid1;
340 
341 	trace_syscalls = 1;
342 	use_vfork = 0;
343 	while ((c = getopt(argc, argv, "csv")) != -1) {
344 		switch (c) {
345 		case 'c':
346 			trace_syscalls = 0;
347 			break;
348 		case 's':
349 			trace_syscalls = 1;
350 			break;
351 		case 'v':
352 			use_vfork = 1;
353 			break;
354 		default:
355 		case '?':
356 			fprintf(stderr, "Usage: %s [-c] [-s] [-v]\n", argv[0]);
357 			return (2);
358 		}
359 	}
360 
361 	if ((pid = fork()) < 0) {
362 		perror("fork");
363 		return 1;
364 	}
365 	else if (pid == 0) {
366 		if (ptrace(PT_TRACE_ME, 0, NULL, 0) < 0) {
367 			perror("PT_TRACE_ME");
368 			_exit(1);
369 		}
370 		kill(getpid(), SIGSTOP);
371 		getpid();
372 		if ((pid1 = use_vfork ? vfork() : fork()) < 0) {
373 			perror("fork1");
374 			return (1);
375 		} else if (pid1 == 0) {
376 			printf("Hi from child %d\n", getpid());
377 			execl("/bin/ls", "ls", "/", (char *)NULL);
378 		}
379 	}
380 	else { /* parent */
381 		if (waitpid(pid, &status, 0) == -1) {
382 			perror("waitpid");
383 			return (-1);
384 		}
385 		assert(WIFSTOPPED(status));
386 		assert(WSTOPSIG(status) == SIGSTOP);
387 
388 		if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo,
389 		    sizeof(lwpinfo)) < 0) {
390 			perror("PT_LWPINFO");
391 			ptrace(PT_KILL, pid, NULL, 0);
392 			return (-1);
393 		}
394 		wait_info(pid, status, &lwpinfo);
395 
396 		if (ptrace(PT_FOLLOW_FORK, pid, 0, 1) < 0) {
397 			perror("PT_FOLLOW_FORK");
398 			ptrace(PT_KILL, pid, NULL, 0);
399 			return (2);
400 		}
401 
402 		while ((pid1 = trace(pid)) >= 0) {
403 			if (pid1 != 0) {
404 				printf(TRACE "attached to pid %d\n", pid1);
405 #if 0
406 				kill(pid1, SIGCONT);
407 #endif
408 				if (waitpid(pid1, &status, 0) == -1) {
409 					perror("waitpid");
410 					return (-1);
411 				}
412 				printf(TRACE "nested loop, pid %d status %s\n",
413 				    pid1, decode_wait_status(status));
414 				assert(WIFSTOPPED(status));
415 				assert(WSTOPSIG(status) == SIGSTOP);
416 				if (ptrace(PT_LWPINFO, pid1, (caddr_t)&lwpinfo,
417 				    sizeof(lwpinfo)) < 0) {
418 					perror("PT_LWPINFO");
419 					ptrace(PT_KILL, pid1, NULL, 0);
420 					return (-1);
421 				}
422 				wait_info(pid1, status, &lwpinfo);
423 
424 				while (trace(pid1) >= 0)
425 					;
426 			}
427 		}
428 
429 		ptrace(PT_CONTINUE, pid, (caddr_t)1, 0);
430 	}
431 	return (0);
432 }
433