1231443Skib/*-
2231443Skib * Copyright (c) 2011, 2012 Konstantin Belousov <kib@FreeBSD.org>
3231443Skib *
4231443Skib * Redistribution and use in source and binary forms, with or without
5231443Skib * modification, are permitted provided that the following conditions
6231443Skib * are met:
7231443Skib * 1. Redistributions of source code must retain the above copyright
8231443Skib *    notice, this list of conditions and the following disclaimer.
9231443Skib * 2. Redistributions in binary form must reproduce the above copyright
10231443Skib *    notice, this list of conditions and the following disclaimer in the
11231443Skib *    documentation and/or other materials provided with the distribution.
12231443Skib *
13231443Skib * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14231443Skib * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15231443Skib * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16231443Skib * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17231443Skib * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18231443Skib * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19231443Skib * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20231443Skib * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21231443Skib * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22231443Skib * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23231443Skib * SUCH DAMAGE.
24231443Skib */
25231443Skib
26231443Skib#include <sys/cdefs.h>
27231443Skib__FBSDID("$FreeBSD$");
28231443Skib
29231443Skib#include <sys/types.h>
30231443Skib#include <sys/ptrace.h>
31231443Skib#include <sys/sysctl.h>
32231443Skib#include <sys/wait.h>
33231443Skib#include <assert.h>
34231443Skib#include <errno.h>
35231443Skib#include <signal.h>
36231443Skib#include <stdio.h>
37231443Skib#include <stdlib.h>
38231443Skib#include <string.h>
39231443Skib#include <unistd.h>
40231443Skib
41231443Skib#define TRACE	">>>> "
42231443Skib
43231443Skibstatic const char *
44231443Skibdecode_wait_status(int status)
45231443Skib{
46231443Skib	static char c[128];
47231443Skib	char b[32];
48231443Skib	int first;
49231443Skib
50231443Skib	c[0] = '\0';
51231443Skib	first = 1;
52231443Skib	if (WIFCONTINUED(status)) {
53231443Skib		first = 0;
54231443Skib		strlcat(c, "CONT", sizeof(c));
55231443Skib	}
56231443Skib	if (WIFEXITED(status)) {
57231443Skib		if (first)
58231443Skib			first = 0;
59231443Skib		else
60231443Skib			strlcat(c, ",", sizeof(c));
61231443Skib		snprintf(b, sizeof(b), "EXIT(%d)", WEXITSTATUS(status));
62231443Skib		strlcat(c, b, sizeof(c));
63231443Skib	}
64231443Skib	if (WIFSIGNALED(status)) {
65231443Skib		if (first)
66231443Skib			first = 0;
67231443Skib		else
68231443Skib			strlcat(c, ",", sizeof(c));
69231443Skib		snprintf(b, sizeof(b), "SIG(%s)", strsignal(WTERMSIG(status)));
70231443Skib		strlcat(c, b, sizeof(c));
71231443Skib		if (WCOREDUMP(status))
72231443Skib			strlcat(c, ",CORE", sizeof(c));
73231443Skib	}
74231443Skib	if (WIFSTOPPED(status)) {
75231443Skib		if (first)
76231443Skib			first = 0;
77231443Skib		else
78231443Skib			strlcat(c, ",", sizeof(c));
79231443Skib		snprintf(b, sizeof(b), "SIG(%s)", strsignal(WSTOPSIG(status)));
80231443Skib		strlcat(c, b, sizeof(c));
81231443Skib	}
82231443Skib	return (c);
83231443Skib}
84231443Skib
85231443Skibstatic const char *
86231443Skibdecode_pl_flags(struct ptrace_lwpinfo *lwpinfo)
87231443Skib{
88231443Skib	static char c[128];
89231443Skib	static struct decode_tag {
90231443Skib		int flag;
91231443Skib		const char *desc;
92231443Skib	} decode[] = {
93231443Skib		{ PL_FLAG_SA, "SA" },
94231443Skib		{ PL_FLAG_BOUND, "BOUND" },
95231443Skib		{ PL_FLAG_SCE, "SCE" },
96231443Skib		{ PL_FLAG_SCX, "SCX" },
97231443Skib		{ PL_FLAG_EXEC, "EXEC" },
98231443Skib		{ PL_FLAG_SI, "SI" },
99231443Skib		{ PL_FLAG_FORKED, "FORKED" },
100231443Skib	};
101231443Skib	char de[32];
102231443Skib	unsigned first, flags, i;
103231443Skib
104231443Skib	c[0] = '\0';
105231443Skib	first = 1;
106231443Skib	flags = lwpinfo->pl_flags;
107231443Skib	for (i = 0; i < sizeof(decode) / sizeof(decode[0]); i++) {
108231443Skib		if ((flags & decode[i].flag) != 0) {
109231443Skib			if (first)
110231443Skib				first = 0;
111231443Skib			else
112231443Skib				strlcat(c, ",", sizeof(c));
113231443Skib			strlcat(c, decode[i].desc, sizeof(c));
114231443Skib			flags &= ~decode[i].flag;
115231443Skib		}
116231443Skib	}
117231443Skib	for (i = 0; i < sizeof(flags) * NBBY; i++) {
118231443Skib		if ((flags & (1 << i)) != 0) {
119231443Skib			if (first)
120231443Skib				first = 0;
121231443Skib			else
122231443Skib				strlcat(c, ",", sizeof(c));
123231443Skib			snprintf(de, sizeof(de), "<%d>", i);
124231443Skib			strlcat(c, de, sizeof(c));
125231443Skib		}
126231443Skib	}
127231443Skib	return (c);
128231443Skib}
129231443Skib
130231443Skibstatic const char *
131231443Skibdecode_pl_event(struct ptrace_lwpinfo *lwpinfo)
132231443Skib{
133231443Skib
134231443Skib	switch (lwpinfo->pl_event) {
135231443Skib	case PL_EVENT_NONE:
136231443Skib		return ("NONE");
137231443Skib
138231443Skib	case PL_EVENT_SIGNAL:
139231443Skib		return ("SIG");
140231443Skib
141231443Skib	default:
142231443Skib		return ("UNKNOWN");
143231443Skib	}
144231443Skib}
145231443Skib
146231443Skibstatic void
147231443Skibget_pathname(pid_t pid)
148231443Skib{
149231443Skib	char pathname[PATH_MAX];
150231443Skib	int error, name[4];
151231443Skib	size_t len;
152231443Skib
153231443Skib	name[0] = CTL_KERN;
154231443Skib	name[1] = KERN_PROC;
155231443Skib	name[2] = KERN_PROC_PATHNAME;
156231443Skib	name[3] = pid;
157231443Skib
158231443Skib	len = sizeof(pathname);
159231443Skib	error = sysctl(name, 4, pathname, &len, NULL, 0);
160231443Skib	if (error < 0) {
161231443Skib		if (errno != ESRCH) {
162231443Skib			fprintf(stderr, "sysctl kern.proc.pathname.%d: %s\n",
163231443Skib			    pid, strerror(errno));
164231443Skib			return;
165231443Skib		}
166231443Skib		fprintf(stderr, "pid %d exited\n", pid);
167231443Skib		return;
168231443Skib	}
169231443Skib	if (len == 0 || strlen(pathname) == 0) {
170231443Skib		fprintf(stderr, "No cached pathname for process %d\n", pid);
171231443Skib		return;
172231443Skib	}
173231443Skib	printf(TRACE "pid %d path %s\n", pid, pathname);
174231443Skib}
175231443Skib
176231443Skibstatic void
177231443Skibwait_info(int pid, int status, struct ptrace_lwpinfo *lwpinfo)
178231443Skib{
179231443Skib
180231443Skib	printf(TRACE "pid %d wait %s", pid,
181231443Skib	    decode_wait_status(status));
182231443Skib	if (lwpinfo != NULL) {
183231443Skib		printf(" event %s flags %s",
184231443Skib		    decode_pl_event(lwpinfo), decode_pl_flags(lwpinfo));
185231443Skib	}
186231443Skib	printf("\n");
187231443Skib}
188231443Skib
189231443Skibstatic int
190231443Skibtrace_sc(int pid)
191231443Skib{
192231443Skib	struct ptrace_lwpinfo lwpinfo;
193231443Skib	int status;
194231443Skib
195231443Skib	if (ptrace(PT_TO_SCE, pid, (caddr_t)1, 0) < 0) {
196231443Skib		perror("PT_TO_SCE");
197231443Skib		ptrace(PT_KILL, pid, NULL, 0);
198231443Skib		return (-1);
199231443Skib	}
200231443Skib
201231443Skib	if (waitpid(pid, &status, 0) == -1) {
202231443Skib		perror("waitpid");
203231443Skib		return (-1);
204231443Skib	}
205231443Skib	if (WIFEXITED(status) || WIFSIGNALED(status)) {
206231443Skib		wait_info(pid, status, NULL);
207231443Skib		return (-1);
208231443Skib	}
209231443Skib	assert(WIFSTOPPED(status));
210231443Skib	assert(WSTOPSIG(status) == SIGTRAP);
211231443Skib
212231443Skib	if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) {
213231443Skib		perror("PT_LWPINFO");
214231443Skib		ptrace(PT_KILL, pid, NULL, 0);
215231443Skib		return (-1);
216231443Skib	}
217231443Skib	wait_info(pid, status, &lwpinfo);
218231443Skib	assert(lwpinfo.pl_flags & PL_FLAG_SCE);
219231443Skib
220231443Skib	if (ptrace(PT_TO_SCX, pid, (caddr_t)1, 0) < 0) {
221231443Skib		perror("PT_TO_SCX");
222231443Skib		ptrace(PT_KILL, pid, NULL, 0);
223231443Skib		return (-1);
224231443Skib	}
225231443Skib
226231443Skib	if (waitpid(pid, &status, 0) == -1) {
227231443Skib		perror("waitpid");
228231443Skib		return (-1);
229231443Skib	}
230231443Skib	if (WIFEXITED(status) || WIFSIGNALED(status)) {
231231443Skib		wait_info(pid, status, NULL);
232231443Skib		return (-1);
233231443Skib	}
234231443Skib	assert(WIFSTOPPED(status));
235231443Skib	assert(WSTOPSIG(status) == SIGTRAP);
236231443Skib
237231443Skib	if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) {
238231443Skib		perror("PT_LWPINFO");
239231443Skib		ptrace(PT_KILL, pid, NULL, 0);
240231443Skib		return (-1);
241231443Skib	}
242231443Skib	wait_info(pid, status, &lwpinfo);
243231443Skib	assert(lwpinfo.pl_flags & PL_FLAG_SCX);
244231443Skib
245231443Skib	if (lwpinfo.pl_flags & PL_FLAG_EXEC)
246231443Skib		get_pathname(pid);
247231443Skib
248231443Skib	if (lwpinfo.pl_flags & PL_FLAG_FORKED) {
249231443Skib		printf(TRACE "forked child %d\n", lwpinfo.pl_child_pid);
250231443Skib		return (lwpinfo.pl_child_pid);
251231443Skib	}
252231443Skib	return (0);
253231443Skib}
254231443Skib
255231443Skibstatic int
256231443Skibtrace_cont(int pid)
257231443Skib{
258231443Skib	struct ptrace_lwpinfo lwpinfo;
259231443Skib	int status;
260231443Skib
261231443Skib	if (ptrace(PT_CONTINUE, pid, (caddr_t)1, 0) < 0) {
262231443Skib		perror("PT_CONTINUE");
263231443Skib		ptrace(PT_KILL, pid, NULL, 0);
264231443Skib		return (-1);
265231443Skib	}
266231443Skib
267231443Skib	if (waitpid(pid, &status, 0) == -1) {
268231443Skib		perror("waitpid");
269231443Skib		return (-1);
270231443Skib	}
271231443Skib	if (WIFEXITED(status) || WIFSIGNALED(status)) {
272231443Skib		wait_info(pid, status, NULL);
273231443Skib		return (-1);
274231443Skib	}
275231443Skib	assert(WIFSTOPPED(status));
276231443Skib	assert(WSTOPSIG(status) == SIGTRAP);
277231443Skib
278231443Skib	if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) {
279231443Skib		perror("PT_LWPINFO");
280231443Skib		ptrace(PT_KILL, pid, NULL, 0);
281231443Skib		return (-1);
282231443Skib	}
283231443Skib	wait_info(pid, status, &lwpinfo);
284231443Skib
285231443Skib	if ((lwpinfo.pl_flags & (PL_FLAG_EXEC | PL_FLAG_SCX)) ==
286231443Skib	    (PL_FLAG_EXEC | PL_FLAG_SCX))
287231443Skib		get_pathname(pid);
288231443Skib
289231443Skib	if ((lwpinfo.pl_flags & (PL_FLAG_FORKED | PL_FLAG_SCX)) ==
290231443Skib	    (PL_FLAG_FORKED | PL_FLAG_SCX)) {
291231443Skib		printf(TRACE "forked child %d\n", lwpinfo.pl_child_pid);
292231443Skib		return (lwpinfo.pl_child_pid);
293231443Skib	}
294231443Skib
295231443Skib	return (0);
296231443Skib}
297231443Skib
298231443Skibstatic int trace_syscalls = 1;
299231443Skib
300231443Skibstatic int
301231443Skibtrace(pid_t pid)
302231443Skib{
303231443Skib
304231443Skib	return (trace_syscalls ? trace_sc(pid) : trace_cont(pid));
305231443Skib}
306231443Skib
307231443Skib
308231443Skibint
309231443Skibmain(int argc, char *argv[])
310231443Skib{
311231443Skib	struct ptrace_lwpinfo lwpinfo;
312231443Skib	int c, status, use_vfork;
313231443Skib	pid_t pid, pid1;
314231443Skib
315231443Skib	trace_syscalls = 1;
316231443Skib	use_vfork = 0;
317231443Skib	while ((c = getopt(argc, argv, "csv")) != -1) {
318231443Skib		switch (c) {
319231443Skib		case 'c':
320231443Skib			trace_syscalls = 0;
321231443Skib			break;
322231443Skib		case 's':
323231443Skib			trace_syscalls = 1;
324231443Skib			break;
325231443Skib		case 'v':
326231443Skib			use_vfork = 1;
327231443Skib			break;
328231443Skib		default:
329231443Skib		case '?':
330231443Skib			fprintf(stderr, "Usage: %s [-c] [-s] [-v]\n", argv[0]);
331231443Skib			return (2);
332231443Skib		}
333231443Skib	}
334231443Skib
335231443Skib	if ((pid = fork()) < 0) {
336231443Skib		perror("fork");
337231443Skib		return 1;
338231443Skib	}
339231443Skib	else if (pid == 0) {
340231443Skib		if (ptrace(PT_TRACE_ME, 0, NULL, 0) < 0) {
341231443Skib			perror("PT_TRACE_ME");
342231443Skib			_exit(1);
343231443Skib		}
344231443Skib		kill(getpid(), SIGSTOP);
345231443Skib		getpid();
346231443Skib		if ((pid1 = use_vfork ? vfork() : fork()) < 0) {
347231443Skib			perror("fork1");
348231443Skib			return (1);
349231443Skib		} else if (pid1 == 0) {
350231443Skib			printf("Hi from child %d\n", getpid());
351231443Skib			execl("/bin/ls", "ls", "/", (char *)NULL);
352231443Skib		}
353231443Skib	}
354231443Skib	else { /* parent */
355231443Skib		if (waitpid(pid, &status, 0) == -1) {
356231443Skib			perror("waitpid");
357231443Skib			return (-1);
358231443Skib		}
359231443Skib		assert(WIFSTOPPED(status));
360231443Skib		assert(WSTOPSIG(status) == SIGSTOP);
361231443Skib
362231443Skib		if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo,
363231443Skib		    sizeof(lwpinfo)) < 0) {
364231443Skib			perror("PT_LWPINFO");
365231443Skib			ptrace(PT_KILL, pid, NULL, 0);
366231443Skib			return (-1);
367231443Skib		}
368231443Skib		wait_info(pid, status, &lwpinfo);
369231443Skib
370231443Skib		if (ptrace(PT_FOLLOW_FORK, pid, 0, 1) < 0) {
371231443Skib			perror("PT_FOLLOW_FORK");
372231443Skib			ptrace(PT_KILL, pid, NULL, 0);
373231443Skib			return (2);
374231443Skib		}
375231443Skib
376231443Skib		while ((pid1 = trace(pid)) >= 0) {
377231443Skib			if (pid1 != 0) {
378231443Skib				printf(TRACE "attached to pid %d\n", pid1);
379231443Skib#if 0
380231443Skib				kill(pid1, SIGCONT);
381231443Skib#endif
382231443Skib				if (waitpid(pid1, &status, 0) == -1) {
383231443Skib					perror("waitpid");
384231443Skib					return (-1);
385231443Skib				}
386231443Skib				printf(TRACE "nested loop, pid %d status %s\n",
387231443Skib				    pid1, decode_wait_status(status));
388231443Skib				assert(WIFSTOPPED(status));
389231443Skib				assert(WSTOPSIG(status) == SIGSTOP);
390231443Skib				if (ptrace(PT_LWPINFO, pid1, (caddr_t)&lwpinfo,
391231443Skib				    sizeof(lwpinfo)) < 0) {
392231443Skib					perror("PT_LWPINFO");
393231443Skib					ptrace(PT_KILL, pid1, NULL, 0);
394231443Skib					return (-1);
395231443Skib				}
396231443Skib				wait_info(pid1, status, &lwpinfo);
397231443Skib
398231443Skib				while (trace(pid1) >= 0)
399231443Skib					;
400231443Skib			}
401231443Skib		}
402231443Skib
403231443Skib		ptrace(PT_CONTINUE, pid, (caddr_t)1, 0);
404231443Skib	}
405231443Skib	return (0);
406231443Skib}
407