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