115920Speter/*-
215920Speter * Copyright (c) 2011, 2012 Konstantin Belousov <kib@FreeBSD.org>
315920Speter *
415920Speter * Redistribution and use in source and binary forms, with or without
515920Speter * modification, are permitted provided that the following conditions
615920Speter * are met:
715920Speter * 1. Redistributions of source code must retain the above copyright
815920Speter *    notice, this list of conditions and the following disclaimer.
915920Speter * 2. Redistributions in binary form must reproduce the above copyright
1015920Speter *    notice, this list of conditions and the following disclaimer in the
1115920Speter *    documentation and/or other materials provided with the distribution.
1215920Speter *
1315920Speter * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1415920Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1515920Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1615920Speter * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1715920Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1815920Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
1915920Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2015920Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2115920Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2215920Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2315920Speter * SUCH DAMAGE.
2415920Speter */
2515920Speter
2615920Speter#include <sys/cdefs.h>
2715920Speter__FBSDID("$FreeBSD$");
2815920Speter
2915920Speter#include <sys/types.h>
3015920Speter#include <sys/ptrace.h>
3115920Speter#include <sys/sysctl.h>
3215920Speter#include <sys/wait.h>
3315920Speter#include <assert.h>
3450471Speter#include <errno.h>
3515920Speter#include <signal.h>
3615920Speter#include <stdio.h>
3715920Speter#include <stdlib.h>
3815920Speter#include <string.h>
39#include <unistd.h>
40
41#define TRACE	">>>> "
42
43static const char *
44decode_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
85static const char *
86decode_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	};
101	char de[32];
102	unsigned first, flags, i;
103
104	c[0] = '\0';
105	first = 1;
106	flags = lwpinfo->pl_flags;
107	for (i = 0; i < sizeof(decode) / sizeof(decode[0]); i++) {
108		if ((flags & decode[i].flag) != 0) {
109			if (first)
110				first = 0;
111			else
112				strlcat(c, ",", sizeof(c));
113			strlcat(c, decode[i].desc, sizeof(c));
114			flags &= ~decode[i].flag;
115		}
116	}
117	for (i = 0; i < sizeof(flags) * NBBY; i++) {
118		if ((flags & (1 << i)) != 0) {
119			if (first)
120				first = 0;
121			else
122				strlcat(c, ",", sizeof(c));
123			snprintf(de, sizeof(de), "<%d>", i);
124			strlcat(c, de, sizeof(c));
125		}
126	}
127	return (c);
128}
129
130static const char *
131decode_pl_event(struct ptrace_lwpinfo *lwpinfo)
132{
133
134	switch (lwpinfo->pl_event) {
135	case PL_EVENT_NONE:
136		return ("NONE");
137
138	case PL_EVENT_SIGNAL:
139		return ("SIG");
140
141	default:
142		return ("UNKNOWN");
143	}
144}
145
146static void
147get_pathname(pid_t pid)
148{
149	char pathname[PATH_MAX];
150	int error, name[4];
151	size_t len;
152
153	name[0] = CTL_KERN;
154	name[1] = KERN_PROC;
155	name[2] = KERN_PROC_PATHNAME;
156	name[3] = pid;
157
158	len = sizeof(pathname);
159	error = sysctl(name, 4, pathname, &len, NULL, 0);
160	if (error < 0) {
161		if (errno != ESRCH) {
162			fprintf(stderr, "sysctl kern.proc.pathname.%d: %s\n",
163			    pid, strerror(errno));
164			return;
165		}
166		fprintf(stderr, "pid %d exited\n", pid);
167		return;
168	}
169	if (len == 0 || strlen(pathname) == 0) {
170		fprintf(stderr, "No cached pathname for process %d\n", pid);
171		return;
172	}
173	printf(TRACE "pid %d path %s\n", pid, pathname);
174}
175
176static void
177wait_info(int pid, int status, struct ptrace_lwpinfo *lwpinfo)
178{
179
180	printf(TRACE "pid %d wait %s", pid,
181	    decode_wait_status(status));
182	if (lwpinfo != NULL) {
183		printf(" event %s flags %s",
184		    decode_pl_event(lwpinfo), decode_pl_flags(lwpinfo));
185	}
186	printf("\n");
187}
188
189static int
190trace_sc(int pid)
191{
192	struct ptrace_lwpinfo lwpinfo;
193	int status;
194
195	if (ptrace(PT_TO_SCE, pid, (caddr_t)1, 0) < 0) {
196		perror("PT_TO_SCE");
197		ptrace(PT_KILL, pid, NULL, 0);
198		return (-1);
199	}
200
201	if (waitpid(pid, &status, 0) == -1) {
202		perror("waitpid");
203		return (-1);
204	}
205	if (WIFEXITED(status) || WIFSIGNALED(status)) {
206		wait_info(pid, status, NULL);
207		return (-1);
208	}
209	assert(WIFSTOPPED(status));
210	assert(WSTOPSIG(status) == SIGTRAP);
211
212	if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) {
213		perror("PT_LWPINFO");
214		ptrace(PT_KILL, pid, NULL, 0);
215		return (-1);
216	}
217	wait_info(pid, status, &lwpinfo);
218	assert(lwpinfo.pl_flags & PL_FLAG_SCE);
219
220	if (ptrace(PT_TO_SCX, pid, (caddr_t)1, 0) < 0) {
221		perror("PT_TO_SCX");
222		ptrace(PT_KILL, pid, NULL, 0);
223		return (-1);
224	}
225
226	if (waitpid(pid, &status, 0) == -1) {
227		perror("waitpid");
228		return (-1);
229	}
230	if (WIFEXITED(status) || WIFSIGNALED(status)) {
231		wait_info(pid, status, NULL);
232		return (-1);
233	}
234	assert(WIFSTOPPED(status));
235	assert(WSTOPSIG(status) == SIGTRAP);
236
237	if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) {
238		perror("PT_LWPINFO");
239		ptrace(PT_KILL, pid, NULL, 0);
240		return (-1);
241	}
242	wait_info(pid, status, &lwpinfo);
243	assert(lwpinfo.pl_flags & PL_FLAG_SCX);
244
245	if (lwpinfo.pl_flags & PL_FLAG_EXEC)
246		get_pathname(pid);
247
248	if (lwpinfo.pl_flags & PL_FLAG_FORKED) {
249		printf(TRACE "forked child %d\n", lwpinfo.pl_child_pid);
250		return (lwpinfo.pl_child_pid);
251	}
252	return (0);
253}
254
255static int
256trace_cont(int pid)
257{
258	struct ptrace_lwpinfo lwpinfo;
259	int status;
260
261	if (ptrace(PT_CONTINUE, pid, (caddr_t)1, 0) < 0) {
262		perror("PT_CONTINUE");
263		ptrace(PT_KILL, pid, NULL, 0);
264		return (-1);
265	}
266
267	if (waitpid(pid, &status, 0) == -1) {
268		perror("waitpid");
269		return (-1);
270	}
271	if (WIFEXITED(status) || WIFSIGNALED(status)) {
272		wait_info(pid, status, NULL);
273		return (-1);
274	}
275	assert(WIFSTOPPED(status));
276	assert(WSTOPSIG(status) == SIGTRAP);
277
278	if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) {
279		perror("PT_LWPINFO");
280		ptrace(PT_KILL, pid, NULL, 0);
281		return (-1);
282	}
283	wait_info(pid, status, &lwpinfo);
284
285	if ((lwpinfo.pl_flags & (PL_FLAG_EXEC | PL_FLAG_SCX)) ==
286	    (PL_FLAG_EXEC | PL_FLAG_SCX))
287		get_pathname(pid);
288
289	if ((lwpinfo.pl_flags & (PL_FLAG_FORKED | PL_FLAG_SCX)) ==
290	    (PL_FLAG_FORKED | PL_FLAG_SCX)) {
291		printf(TRACE "forked child %d\n", lwpinfo.pl_child_pid);
292		return (lwpinfo.pl_child_pid);
293	}
294
295	return (0);
296}
297
298static int trace_syscalls = 1;
299
300static int
301trace(pid_t pid)
302{
303
304	return (trace_syscalls ? trace_sc(pid) : trace_cont(pid));
305}
306
307
308int
309main(int argc, char *argv[])
310{
311	struct ptrace_lwpinfo lwpinfo;
312	int c, status, use_vfork;
313	pid_t pid, pid1;
314
315	trace_syscalls = 1;
316	use_vfork = 0;
317	while ((c = getopt(argc, argv, "csv")) != -1) {
318		switch (c) {
319		case 'c':
320			trace_syscalls = 0;
321			break;
322		case 's':
323			trace_syscalls = 1;
324			break;
325		case 'v':
326			use_vfork = 1;
327			break;
328		default:
329		case '?':
330			fprintf(stderr, "Usage: %s [-c] [-s] [-v]\n", argv[0]);
331			return (2);
332		}
333	}
334
335	if ((pid = fork()) < 0) {
336		perror("fork");
337		return 1;
338	}
339	else if (pid == 0) {
340		if (ptrace(PT_TRACE_ME, 0, NULL, 0) < 0) {
341			perror("PT_TRACE_ME");
342			_exit(1);
343		}
344		kill(getpid(), SIGSTOP);
345		getpid();
346		if ((pid1 = use_vfork ? vfork() : fork()) < 0) {
347			perror("fork1");
348			return (1);
349		} else if (pid1 == 0) {
350			printf("Hi from child %d\n", getpid());
351			execl("/bin/ls", "ls", "/", (char *)NULL);
352		}
353	}
354	else { /* parent */
355		if (waitpid(pid, &status, 0) == -1) {
356			perror("waitpid");
357			return (-1);
358		}
359		assert(WIFSTOPPED(status));
360		assert(WSTOPSIG(status) == SIGSTOP);
361
362		if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo,
363		    sizeof(lwpinfo)) < 0) {
364			perror("PT_LWPINFO");
365			ptrace(PT_KILL, pid, NULL, 0);
366			return (-1);
367		}
368		wait_info(pid, status, &lwpinfo);
369
370		if (ptrace(PT_FOLLOW_FORK, pid, 0, 1) < 0) {
371			perror("PT_FOLLOW_FORK");
372			ptrace(PT_KILL, pid, NULL, 0);
373			return (2);
374		}
375
376		while ((pid1 = trace(pid)) >= 0) {
377			if (pid1 != 0) {
378				printf(TRACE "attached to pid %d\n", pid1);
379#if 0
380				kill(pid1, SIGCONT);
381#endif
382				if (waitpid(pid1, &status, 0) == -1) {
383					perror("waitpid");
384					return (-1);
385				}
386				printf(TRACE "nested loop, pid %d status %s\n",
387				    pid1, decode_wait_status(status));
388				assert(WIFSTOPPED(status));
389				assert(WSTOPSIG(status) == SIGSTOP);
390				if (ptrace(PT_LWPINFO, pid1, (caddr_t)&lwpinfo,
391				    sizeof(lwpinfo)) < 0) {
392					perror("PT_LWPINFO");
393					ptrace(PT_KILL, pid1, NULL, 0);
394					return (-1);
395				}
396				wait_info(pid1, status, &lwpinfo);
397
398				while (trace(pid1) >= 0)
399					;
400			}
401		}
402
403		ptrace(PT_CONTINUE, pid, (caddr_t)1, 0);
404	}
405	return (0);
406}
407