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