xref: /freebsd/tools/test/ptrace/scescx.c (revision 8ddb146abcdf061be9f2c0db7e391697dafad85c)
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 				printf("(");
200 				for (i = 0; i < (int)lwpinfo->pl_syscall_narg;
201 				    i++) {
202 					printf("%s%#lx", i == 0 ? "" : ",",
203 					    args[i]);
204 				}
205 				printf(")");
206 			} else {
207 				fprintf(stderr, "PT_GET_SC_ARGS failed: %s",
208 				    strerror(errno));
209 			}
210 			free(args);
211 		}
212 	}
213 	printf("\n");
214 }
215 
216 static int
217 trace_sc(int pid)
218 {
219 	struct ptrace_lwpinfo lwpinfo;
220 	int status;
221 
222 	if (ptrace(PT_TO_SCE, pid, (caddr_t)1, 0) < 0) {
223 		perror("PT_TO_SCE");
224 		ptrace(PT_KILL, pid, NULL, 0);
225 		return (-1);
226 	}
227 
228 	if (waitpid(pid, &status, 0) == -1) {
229 		perror("waitpid");
230 		return (-1);
231 	}
232 	if (WIFEXITED(status) || WIFSIGNALED(status)) {
233 		wait_info(pid, status, NULL);
234 		return (-1);
235 	}
236 	assert(WIFSTOPPED(status));
237 	assert(WSTOPSIG(status) == SIGTRAP);
238 
239 	if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) {
240 		perror("PT_LWPINFO");
241 		ptrace(PT_KILL, pid, NULL, 0);
242 		return (-1);
243 	}
244 	wait_info(pid, status, &lwpinfo);
245 	assert(lwpinfo.pl_flags & PL_FLAG_SCE);
246 
247 	if (ptrace(PT_TO_SCX, pid, (caddr_t)1, 0) < 0) {
248 		perror("PT_TO_SCX");
249 		ptrace(PT_KILL, pid, NULL, 0);
250 		return (-1);
251 	}
252 
253 	if (waitpid(pid, &status, 0) == -1) {
254 		perror("waitpid");
255 		return (-1);
256 	}
257 	if (WIFEXITED(status) || WIFSIGNALED(status)) {
258 		wait_info(pid, status, NULL);
259 		return (-1);
260 	}
261 	assert(WIFSTOPPED(status));
262 	assert(WSTOPSIG(status) == SIGTRAP);
263 
264 	if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) {
265 		perror("PT_LWPINFO");
266 		ptrace(PT_KILL, pid, NULL, 0);
267 		return (-1);
268 	}
269 	wait_info(pid, status, &lwpinfo);
270 	assert(lwpinfo.pl_flags & PL_FLAG_SCX);
271 
272 	if (lwpinfo.pl_flags & PL_FLAG_EXEC)
273 		get_pathname(pid);
274 
275 	if (lwpinfo.pl_flags & PL_FLAG_FORKED) {
276 		printf(TRACE "forked child %d\n", lwpinfo.pl_child_pid);
277 		return (lwpinfo.pl_child_pid);
278 	}
279 	return (0);
280 }
281 
282 static int
283 trace_cont(int pid)
284 {
285 	struct ptrace_lwpinfo lwpinfo;
286 	int status;
287 
288 	if (ptrace(PT_CONTINUE, pid, (caddr_t)1, 0) < 0) {
289 		perror("PT_CONTINUE");
290 		ptrace(PT_KILL, pid, NULL, 0);
291 		return (-1);
292 	}
293 
294 	if (waitpid(pid, &status, 0) == -1) {
295 		perror("waitpid");
296 		return (-1);
297 	}
298 	if (WIFEXITED(status) || WIFSIGNALED(status)) {
299 		wait_info(pid, status, NULL);
300 		return (-1);
301 	}
302 	assert(WIFSTOPPED(status));
303 	assert(WSTOPSIG(status) == SIGTRAP);
304 
305 	if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) {
306 		perror("PT_LWPINFO");
307 		ptrace(PT_KILL, pid, NULL, 0);
308 		return (-1);
309 	}
310 	wait_info(pid, status, &lwpinfo);
311 
312 	if ((lwpinfo.pl_flags & (PL_FLAG_EXEC | PL_FLAG_SCX)) ==
313 	    (PL_FLAG_EXEC | PL_FLAG_SCX))
314 		get_pathname(pid);
315 
316 	if ((lwpinfo.pl_flags & (PL_FLAG_FORKED | PL_FLAG_SCX)) ==
317 	    (PL_FLAG_FORKED | PL_FLAG_SCX)) {
318 		printf(TRACE "forked child %d\n", lwpinfo.pl_child_pid);
319 		return (lwpinfo.pl_child_pid);
320 	}
321 
322 	return (0);
323 }
324 
325 static int trace_syscalls = 1;
326 
327 static int
328 trace(pid_t pid)
329 {
330 
331 	return (trace_syscalls ? trace_sc(pid) : trace_cont(pid));
332 }
333 
334 
335 int
336 main(int argc, char *argv[])
337 {
338 	struct ptrace_lwpinfo lwpinfo;
339 	int c, status, use_vfork;
340 	pid_t pid, pid1;
341 
342 	trace_syscalls = 1;
343 	use_vfork = 0;
344 	while ((c = getopt(argc, argv, "csv")) != -1) {
345 		switch (c) {
346 		case 'c':
347 			trace_syscalls = 0;
348 			break;
349 		case 's':
350 			trace_syscalls = 1;
351 			break;
352 		case 'v':
353 			use_vfork = 1;
354 			break;
355 		default:
356 		case '?':
357 			fprintf(stderr, "Usage: %s [-c] [-s] [-v]\n", argv[0]);
358 			return (2);
359 		}
360 	}
361 
362 	if ((pid = fork()) < 0) {
363 		perror("fork");
364 		return 1;
365 	}
366 	else if (pid == 0) {
367 		if (ptrace(PT_TRACE_ME, 0, NULL, 0) < 0) {
368 			perror("PT_TRACE_ME");
369 			_exit(1);
370 		}
371 		kill(getpid(), SIGSTOP);
372 		getpid();
373 		if ((pid1 = use_vfork ? vfork() : fork()) < 0) {
374 			perror("fork1");
375 			return (1);
376 		} else if (pid1 == 0) {
377 			printf("Hi from child %d\n", getpid());
378 			execl("/bin/ls", "ls", "/", (char *)NULL);
379 		}
380 	}
381 	else { /* parent */
382 		if (waitpid(pid, &status, 0) == -1) {
383 			perror("waitpid");
384 			return (-1);
385 		}
386 		assert(WIFSTOPPED(status));
387 		assert(WSTOPSIG(status) == SIGSTOP);
388 
389 		if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo,
390 		    sizeof(lwpinfo)) < 0) {
391 			perror("PT_LWPINFO");
392 			ptrace(PT_KILL, pid, NULL, 0);
393 			return (-1);
394 		}
395 		wait_info(pid, status, &lwpinfo);
396 
397 		if (ptrace(PT_FOLLOW_FORK, pid, 0, 1) < 0) {
398 			perror("PT_FOLLOW_FORK");
399 			ptrace(PT_KILL, pid, NULL, 0);
400 			return (2);
401 		}
402 
403 		while ((pid1 = trace(pid)) >= 0) {
404 			if (pid1 != 0) {
405 				printf(TRACE "attached to pid %d\n", pid1);
406 #if 0
407 				kill(pid1, SIGCONT);
408 #endif
409 				if (waitpid(pid1, &status, 0) == -1) {
410 					perror("waitpid");
411 					return (-1);
412 				}
413 				printf(TRACE "nested loop, pid %d status %s\n",
414 				    pid1, decode_wait_status(status));
415 				assert(WIFSTOPPED(status));
416 				assert(WSTOPSIG(status) == SIGSTOP);
417 				if (ptrace(PT_LWPINFO, pid1, (caddr_t)&lwpinfo,
418 				    sizeof(lwpinfo)) < 0) {
419 					perror("PT_LWPINFO");
420 					ptrace(PT_KILL, pid1, NULL, 0);
421 					return (-1);
422 				}
423 				wait_info(pid1, status, &lwpinfo);
424 
425 				while (trace(pid1) >= 0)
426 					;
427 			}
428 		}
429 
430 		ptrace(PT_CONTINUE, pid, (caddr_t)1, 0);
431 	}
432 	return (0);
433 }
434