xref: /freebsd/tools/test/ptrace/scescx.c (revision b3e7694832e81d7a904a10f525f8797b753bf0d3)
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/syscall.h>
32 #include <sys/sysctl.h>
33 #include <sys/wait.h>
34 #include <assert.h>
35 #include <errno.h>
36 #include <signal.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <unistd.h>
41 
42 #define TRACE	">>>> "
43 
44 static const char *
45 decode_wait_status(int status)
46 {
47 	static char c[128];
48 	char b[32];
49 	int first;
50 
51 	c[0] = '\0';
52 	first = 1;
53 	if (WIFCONTINUED(status)) {
54 		first = 0;
55 		strlcat(c, "CONT", sizeof(c));
56 	}
57 	if (WIFEXITED(status)) {
58 		if (first)
59 			first = 0;
60 		else
61 			strlcat(c, ",", sizeof(c));
62 		snprintf(b, sizeof(b), "EXIT(%d)", WEXITSTATUS(status));
63 		strlcat(c, b, sizeof(c));
64 	}
65 	if (WIFSIGNALED(status)) {
66 		if (first)
67 			first = 0;
68 		else
69 			strlcat(c, ",", sizeof(c));
70 		snprintf(b, sizeof(b), "SIG(%s)", strsignal(WTERMSIG(status)));
71 		strlcat(c, b, sizeof(c));
72 		if (WCOREDUMP(status))
73 			strlcat(c, ",CORE", sizeof(c));
74 	}
75 	if (WIFSTOPPED(status)) {
76 		if (first)
77 			first = 0;
78 		else
79 			strlcat(c, ",", sizeof(c));
80 		snprintf(b, sizeof(b), "SIG(%s)", strsignal(WSTOPSIG(status)));
81 		strlcat(c, b, sizeof(c));
82 	}
83 	return (c);
84 }
85 
86 static const char *
87 decode_pl_flags(struct ptrace_lwpinfo *lwpinfo)
88 {
89 	static char c[128];
90 	static struct decode_tag {
91 		int flag;
92 		const char *desc;
93 	} decode[] = {
94 		{ PL_FLAG_SA, "SA" },
95 		{ PL_FLAG_BOUND, "BOUND" },
96 		{ PL_FLAG_SCE, "SCE" },
97 		{ PL_FLAG_SCX, "SCX" },
98 		{ PL_FLAG_EXEC, "EXEC" },
99 		{ PL_FLAG_SI, "SI" },
100 		{ PL_FLAG_FORKED, "FORKED" },
101 		{ PL_FLAG_CHILD, "CHILD" },
102 		{ PL_FLAG_BORN, "LWPBORN" },
103 		{ PL_FLAG_EXITED, "LWPEXITED" },
104 		{ PL_FLAG_VFORKED, "VFORKED" },
105 		{ PL_FLAG_VFORK_DONE, "VFORKDONE" },
106 	};
107 	char de[32];
108 	unsigned first, flags, i;
109 
110 	c[0] = '\0';
111 	first = 1;
112 	flags = lwpinfo->pl_flags;
113 	for (i = 0; i < sizeof(decode) / sizeof(decode[0]); i++) {
114 		if ((flags & decode[i].flag) != 0) {
115 			if (first)
116 				first = 0;
117 			else
118 				strlcat(c, ",", sizeof(c));
119 			strlcat(c, decode[i].desc, sizeof(c));
120 			flags &= ~decode[i].flag;
121 		}
122 	}
123 	for (i = 0; i < sizeof(flags) * NBBY; i++) {
124 		if ((flags & (1 << i)) != 0) {
125 			if (first)
126 				first = 0;
127 			else
128 				strlcat(c, ",", sizeof(c));
129 			snprintf(de, sizeof(de), "<%d>", i);
130 			strlcat(c, de, sizeof(c));
131 		}
132 	}
133 	return (c);
134 }
135 
136 static const char *
137 decode_pl_event(struct ptrace_lwpinfo *lwpinfo)
138 {
139 
140 	switch (lwpinfo->pl_event) {
141 	case PL_EVENT_NONE:
142 		return ("NONE");
143 
144 	case PL_EVENT_SIGNAL:
145 		return ("SIG");
146 
147 	default:
148 		return ("UNKNOWN");
149 	}
150 }
151 
152 static void
153 get_pathname(pid_t pid)
154 {
155 	char pathname[PATH_MAX];
156 	int error, name[4];
157 	size_t len;
158 
159 	name[0] = CTL_KERN;
160 	name[1] = KERN_PROC;
161 	name[2] = KERN_PROC_PATHNAME;
162 	name[3] = pid;
163 
164 	len = sizeof(pathname);
165 	error = sysctl(name, 4, pathname, &len, NULL, 0);
166 	if (error < 0) {
167 		if (errno != ESRCH) {
168 			fprintf(stderr, "sysctl kern.proc.pathname.%d: %s\n",
169 			    pid, strerror(errno));
170 			return;
171 		}
172 		fprintf(stderr, "pid %d exited\n", pid);
173 		return;
174 	}
175 	if (len == 0 || strlen(pathname) == 0) {
176 		fprintf(stderr, "No cached pathname for process %d\n", pid);
177 		return;
178 	}
179 	printf(TRACE "pid %d path %s\n", pid, pathname);
180 }
181 
182 static void
183 wait_info(int pid, int status, struct ptrace_lwpinfo *lwpinfo)
184 {
185 	long *args;
186 	int error, i;
187 
188 	printf(TRACE "pid %d wait %s", pid,
189 	    decode_wait_status(status));
190 	if (lwpinfo != NULL) {
191 		printf(" event %s flags %s",
192 		    decode_pl_event(lwpinfo), decode_pl_flags(lwpinfo));
193 		if ((lwpinfo->pl_flags & (PL_FLAG_SCE | PL_FLAG_SCX)) != 0) {
194 			printf(" sc%d", lwpinfo->pl_syscall_code);
195 			args = calloc(lwpinfo->pl_syscall_narg, sizeof(long));
196 			error = ptrace(PT_GET_SC_ARGS, lwpinfo->pl_lwpid,
197 			    (caddr_t)args, lwpinfo->pl_syscall_narg *
198 			    sizeof(long));
199 			if (error == 0) {
200 				printf("(");
201 				for (i = 0; i < (int)lwpinfo->pl_syscall_narg;
202 				    i++) {
203 					printf("%s%#lx", i == 0 ? "" : ",",
204 					    args[i]);
205 				}
206 				printf(")");
207 			} else {
208 				fprintf(stderr, "PT_GET_SC_ARGS failed: %s",
209 				    strerror(errno));
210 			}
211 			free(args);
212 		}
213 	}
214 	printf("\n");
215 }
216 
217 static int trace_syscalls = 1;
218 static int remote_getpid = 0;
219 
220 static int
221 trace_sc(int pid)
222 {
223 	struct ptrace_sc_remote pscr;
224 	struct ptrace_lwpinfo lwpinfo;
225 	int status;
226 
227 	if (ptrace(PT_TO_SCE, pid, (caddr_t)1, 0) < 0) {
228 		perror("PT_TO_SCE");
229 		ptrace(PT_KILL, pid, NULL, 0);
230 		return (-1);
231 	}
232 
233 	if (waitpid(pid, &status, 0) == -1) {
234 		perror("waitpid");
235 		return (-1);
236 	}
237 	if (WIFEXITED(status) || WIFSIGNALED(status)) {
238 		wait_info(pid, status, NULL);
239 		return (-1);
240 	}
241 	assert(WIFSTOPPED(status));
242 	assert(WSTOPSIG(status) == SIGTRAP);
243 
244 	if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) {
245 		perror("PT_LWPINFO");
246 		ptrace(PT_KILL, pid, NULL, 0);
247 		return (-1);
248 	}
249 	wait_info(pid, status, &lwpinfo);
250 	assert(lwpinfo.pl_flags & PL_FLAG_SCE);
251 
252 	if (ptrace(PT_TO_SCX, pid, (caddr_t)1, 0) < 0) {
253 		perror("PT_TO_SCX");
254 		ptrace(PT_KILL, pid, NULL, 0);
255 		return (-1);
256 	}
257 
258 	if (waitpid(pid, &status, 0) == -1) {
259 		perror("waitpid");
260 		return (-1);
261 	}
262 	if (WIFEXITED(status) || WIFSIGNALED(status)) {
263 		wait_info(pid, status, NULL);
264 		return (-1);
265 	}
266 	assert(WIFSTOPPED(status));
267 	assert(WSTOPSIG(status) == SIGTRAP);
268 
269 	if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) {
270 		perror("PT_LWPINFO");
271 		ptrace(PT_KILL, pid, NULL, 0);
272 		return (-1);
273 	}
274 	wait_info(pid, status, &lwpinfo);
275 	assert(lwpinfo.pl_flags & PL_FLAG_SCX);
276 
277 	if (remote_getpid) {
278 		memset(&pscr, 0, sizeof(pscr));
279 		pscr.pscr_syscall = SYS_getpid;
280 		pscr.pscr_nargs = 0;
281 		if (ptrace(PT_SC_REMOTE, pid, (caddr_t)&pscr,
282 		    sizeof(pscr)) < 0) {
283 			perror("PT_SC_REMOTE");
284 			ptrace(PT_KILL, pid, NULL, 0);
285 			return (-1);
286 		} else {
287 			printf(TRACE "remote getpid %ld errno %d\n",
288 			    pscr.pscr_ret.sr_retval[0], pscr.pscr_ret.sr_error);
289 			if (waitpid(pid, &status, 0) == -1) {
290 			  perror("waitpid");
291 			  return (-1);
292 			}
293 		}
294 	}
295 	if (lwpinfo.pl_flags & PL_FLAG_EXEC)
296 		get_pathname(pid);
297 
298 	if (lwpinfo.pl_flags & PL_FLAG_FORKED) {
299 		printf(TRACE "forked child %d\n", lwpinfo.pl_child_pid);
300 		return (lwpinfo.pl_child_pid);
301 	}
302 	return (0);
303 }
304 
305 static int
306 trace_cont(int pid)
307 {
308 	struct ptrace_lwpinfo lwpinfo;
309 	int status;
310 
311 	if (ptrace(PT_CONTINUE, pid, (caddr_t)1, 0) < 0) {
312 		perror("PT_CONTINUE");
313 		ptrace(PT_KILL, pid, NULL, 0);
314 		return (-1);
315 	}
316 
317 	if (waitpid(pid, &status, 0) == -1) {
318 		perror("waitpid");
319 		return (-1);
320 	}
321 	if (WIFEXITED(status) || WIFSIGNALED(status)) {
322 		wait_info(pid, status, NULL);
323 		return (-1);
324 	}
325 	assert(WIFSTOPPED(status));
326 	assert(WSTOPSIG(status) == SIGTRAP);
327 
328 	if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) {
329 		perror("PT_LWPINFO");
330 		ptrace(PT_KILL, pid, NULL, 0);
331 		return (-1);
332 	}
333 	wait_info(pid, status, &lwpinfo);
334 
335 	if ((lwpinfo.pl_flags & (PL_FLAG_EXEC | PL_FLAG_SCX)) ==
336 	    (PL_FLAG_EXEC | PL_FLAG_SCX))
337 		get_pathname(pid);
338 
339 	if ((lwpinfo.pl_flags & (PL_FLAG_FORKED | PL_FLAG_SCX)) ==
340 	    (PL_FLAG_FORKED | PL_FLAG_SCX)) {
341 		printf(TRACE "forked child %d\n", lwpinfo.pl_child_pid);
342 		return (lwpinfo.pl_child_pid);
343 	}
344 
345 	return (0);
346 }
347 
348 static int
349 trace(pid_t pid)
350 {
351 
352 	return (trace_syscalls ? trace_sc(pid) : trace_cont(pid));
353 }
354 
355 
356 int
357 main(int argc, char *argv[])
358 {
359 	struct ptrace_lwpinfo lwpinfo;
360 	int c, status, use_vfork;
361 	pid_t pid, pid1;
362 
363 	trace_syscalls = 1;
364 	remote_getpid = 0;
365 	use_vfork = 0;
366 	while ((c = getopt(argc, argv, "crsv")) != -1) {
367 		switch (c) {
368 		case 'c':
369 			trace_syscalls = 0;
370 			break;
371 		case 'r':
372 			remote_getpid = 1;
373 			break;
374 		case 's':
375 			trace_syscalls = 1;
376 			break;
377 		case 'v':
378 			use_vfork = 1;
379 			break;
380 		default:
381 		case '?':
382 			fprintf(stderr, "Usage: %s [-c] [-r] [-s] [-v]\n",
383 			    argv[0]);
384 			return (2);
385 		}
386 	}
387 
388 	if ((pid = fork()) < 0) {
389 		perror("fork");
390 		return 1;
391 	}
392 	else if (pid == 0) {
393 		if (ptrace(PT_TRACE_ME, 0, NULL, 0) < 0) {
394 			perror("PT_TRACE_ME");
395 			_exit(1);
396 		}
397 		kill(getpid(), SIGSTOP);
398 		getpid();
399 		if ((pid1 = use_vfork ? vfork() : fork()) < 0) {
400 			perror("fork1");
401 			return (1);
402 		} else if (pid1 == 0) {
403 			printf("Hi from child %d\n", getpid());
404 			execl("/bin/ls", "ls", "/", (char *)NULL);
405 		}
406 	}
407 	else { /* parent */
408 		if (waitpid(pid, &status, 0) == -1) {
409 			perror("waitpid");
410 			return (-1);
411 		}
412 		assert(WIFSTOPPED(status));
413 		assert(WSTOPSIG(status) == SIGSTOP);
414 
415 		if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo,
416 		    sizeof(lwpinfo)) < 0) {
417 			perror("PT_LWPINFO");
418 			ptrace(PT_KILL, pid, NULL, 0);
419 			return (-1);
420 		}
421 		wait_info(pid, status, &lwpinfo);
422 
423 		if (ptrace(PT_FOLLOW_FORK, pid, 0, 1) < 0) {
424 			perror("PT_FOLLOW_FORK");
425 			ptrace(PT_KILL, pid, NULL, 0);
426 			return (2);
427 		}
428 
429 		while ((pid1 = trace(pid)) >= 0) {
430 			if (pid1 != 0) {
431 				printf(TRACE "attached to pid %d\n", pid1);
432 #if 0
433 				kill(pid1, SIGCONT);
434 #endif
435 				if (waitpid(pid1, &status, 0) == -1) {
436 					perror("waitpid");
437 					return (-1);
438 				}
439 				printf(TRACE "nested loop, pid %d status %s\n",
440 				    pid1, decode_wait_status(status));
441 				assert(WIFSTOPPED(status));
442 				assert(WSTOPSIG(status) == SIGSTOP);
443 				if (ptrace(PT_LWPINFO, pid1, (caddr_t)&lwpinfo,
444 				    sizeof(lwpinfo)) < 0) {
445 					perror("PT_LWPINFO");
446 					ptrace(PT_KILL, pid1, NULL, 0);
447 					return (-1);
448 				}
449 				wait_info(pid1, status, &lwpinfo);
450 
451 				while (trace(pid1) >= 0)
452 					;
453 			}
454 		}
455 
456 		ptrace(PT_CONTINUE, pid, (caddr_t)1, 0);
457 	}
458 	return (0);
459 }
460