scescx.c revision 321346
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: stable/11/tools/test/ptrace/scescx.c 321346 2017-07-21 18:14:06Z kib $");
28
29#include <sys/types.h>
30#include <sys/ptrace.h>
31#include <sys/sysctl.h>
32#include <sys/wait.h>
33#include <assert.h>
34#include <errno.h>
35#include <signal.h>
36#include <stdio.h>
37#include <stdlib.h>
38#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		{ PL_FLAG_CHILD, "CHILD" },
101		{ PL_FLAG_BORN, "LWPBORN" },
102		{ PL_FLAG_EXITED, "LWPEXITED" },
103		{ PL_FLAG_VFORKED, "VFORKED" },
104		{ PL_FLAG_VFORK_DONE, "VFORKDONE" },
105	};
106	char de[32];
107	unsigned first, flags, i;
108
109	c[0] = '\0';
110	first = 1;
111	flags = lwpinfo->pl_flags;
112	for (i = 0; i < sizeof(decode) / sizeof(decode[0]); i++) {
113		if ((flags & decode[i].flag) != 0) {
114			if (first)
115				first = 0;
116			else
117				strlcat(c, ",", sizeof(c));
118			strlcat(c, decode[i].desc, sizeof(c));
119			flags &= ~decode[i].flag;
120		}
121	}
122	for (i = 0; i < sizeof(flags) * NBBY; i++) {
123		if ((flags & (1 << i)) != 0) {
124			if (first)
125				first = 0;
126			else
127				strlcat(c, ",", sizeof(c));
128			snprintf(de, sizeof(de), "<%d>", i);
129			strlcat(c, de, sizeof(c));
130		}
131	}
132	return (c);
133}
134
135static const char *
136decode_pl_event(struct ptrace_lwpinfo *lwpinfo)
137{
138
139	switch (lwpinfo->pl_event) {
140	case PL_EVENT_NONE:
141		return ("NONE");
142
143	case PL_EVENT_SIGNAL:
144		return ("SIG");
145
146	default:
147		return ("UNKNOWN");
148	}
149}
150
151static void
152get_pathname(pid_t pid)
153{
154	char pathname[PATH_MAX];
155	int error, name[4];
156	size_t len;
157
158	name[0] = CTL_KERN;
159	name[1] = KERN_PROC;
160	name[2] = KERN_PROC_PATHNAME;
161	name[3] = pid;
162
163	len = sizeof(pathname);
164	error = sysctl(name, 4, pathname, &len, NULL, 0);
165	if (error < 0) {
166		if (errno != ESRCH) {
167			fprintf(stderr, "sysctl kern.proc.pathname.%d: %s\n",
168			    pid, strerror(errno));
169			return;
170		}
171		fprintf(stderr, "pid %d exited\n", pid);
172		return;
173	}
174	if (len == 0 || strlen(pathname) == 0) {
175		fprintf(stderr, "No cached pathname for process %d\n", pid);
176		return;
177	}
178	printf(TRACE "pid %d path %s\n", pid, pathname);
179}
180
181static void
182wait_info(int pid, int status, struct ptrace_lwpinfo *lwpinfo)
183{
184	long *args;
185	int error, i;
186
187	printf(TRACE "pid %d wait %s", pid,
188	    decode_wait_status(status));
189	if (lwpinfo != NULL) {
190		printf(" event %s flags %s",
191		    decode_pl_event(lwpinfo), decode_pl_flags(lwpinfo));
192		if ((lwpinfo->pl_flags & (PL_FLAG_SCE | PL_FLAG_SCX)) != 0) {
193			printf(" sc%d", lwpinfo->pl_syscall_code);
194			args = calloc(lwpinfo->pl_syscall_narg, sizeof(long));
195			error = ptrace(PT_GET_SC_ARGS, lwpinfo->pl_lwpid,
196			    (caddr_t)args, lwpinfo->pl_syscall_narg *
197			    sizeof(long));
198			if (error == 0) {
199				for (i = 0; i < (int)lwpinfo->pl_syscall_narg;
200				    i++) {
201					printf("%c%#lx", i == 0 ? '(' : ',',
202					    args[i]);
203				}
204			} else {
205				fprintf(stderr, "PT_GET_SC_ARGS failed: %s",
206				    strerror(errno));
207			}
208			printf(")");
209			free(args);
210		}
211	}
212	printf("\n");
213}
214
215static int
216trace_sc(int pid)
217{
218	struct ptrace_lwpinfo lwpinfo;
219	int status;
220
221	if (ptrace(PT_TO_SCE, pid, (caddr_t)1, 0) < 0) {
222		perror("PT_TO_SCE");
223		ptrace(PT_KILL, pid, NULL, 0);
224		return (-1);
225	}
226
227	if (waitpid(pid, &status, 0) == -1) {
228		perror("waitpid");
229		return (-1);
230	}
231	if (WIFEXITED(status) || WIFSIGNALED(status)) {
232		wait_info(pid, status, NULL);
233		return (-1);
234	}
235	assert(WIFSTOPPED(status));
236	assert(WSTOPSIG(status) == SIGTRAP);
237
238	if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) {
239		perror("PT_LWPINFO");
240		ptrace(PT_KILL, pid, NULL, 0);
241		return (-1);
242	}
243	wait_info(pid, status, &lwpinfo);
244	assert(lwpinfo.pl_flags & PL_FLAG_SCE);
245
246	if (ptrace(PT_TO_SCX, pid, (caddr_t)1, 0) < 0) {
247		perror("PT_TO_SCX");
248		ptrace(PT_KILL, pid, NULL, 0);
249		return (-1);
250	}
251
252	if (waitpid(pid, &status, 0) == -1) {
253		perror("waitpid");
254		return (-1);
255	}
256	if (WIFEXITED(status) || WIFSIGNALED(status)) {
257		wait_info(pid, status, NULL);
258		return (-1);
259	}
260	assert(WIFSTOPPED(status));
261	assert(WSTOPSIG(status) == SIGTRAP);
262
263	if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) {
264		perror("PT_LWPINFO");
265		ptrace(PT_KILL, pid, NULL, 0);
266		return (-1);
267	}
268	wait_info(pid, status, &lwpinfo);
269	assert(lwpinfo.pl_flags & PL_FLAG_SCX);
270
271	if (lwpinfo.pl_flags & PL_FLAG_EXEC)
272		get_pathname(pid);
273
274	if (lwpinfo.pl_flags & PL_FLAG_FORKED) {
275		printf(TRACE "forked child %d\n", lwpinfo.pl_child_pid);
276		return (lwpinfo.pl_child_pid);
277	}
278	return (0);
279}
280
281static int
282trace_cont(int pid)
283{
284	struct ptrace_lwpinfo lwpinfo;
285	int status;
286
287	if (ptrace(PT_CONTINUE, pid, (caddr_t)1, 0) < 0) {
288		perror("PT_CONTINUE");
289		ptrace(PT_KILL, pid, NULL, 0);
290		return (-1);
291	}
292
293	if (waitpid(pid, &status, 0) == -1) {
294		perror("waitpid");
295		return (-1);
296	}
297	if (WIFEXITED(status) || WIFSIGNALED(status)) {
298		wait_info(pid, status, NULL);
299		return (-1);
300	}
301	assert(WIFSTOPPED(status));
302	assert(WSTOPSIG(status) == SIGTRAP);
303
304	if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) {
305		perror("PT_LWPINFO");
306		ptrace(PT_KILL, pid, NULL, 0);
307		return (-1);
308	}
309	wait_info(pid, status, &lwpinfo);
310
311	if ((lwpinfo.pl_flags & (PL_FLAG_EXEC | PL_FLAG_SCX)) ==
312	    (PL_FLAG_EXEC | PL_FLAG_SCX))
313		get_pathname(pid);
314
315	if ((lwpinfo.pl_flags & (PL_FLAG_FORKED | PL_FLAG_SCX)) ==
316	    (PL_FLAG_FORKED | PL_FLAG_SCX)) {
317		printf(TRACE "forked child %d\n", lwpinfo.pl_child_pid);
318		return (lwpinfo.pl_child_pid);
319	}
320
321	return (0);
322}
323
324static int trace_syscalls = 1;
325
326static int
327trace(pid_t pid)
328{
329
330	return (trace_syscalls ? trace_sc(pid) : trace_cont(pid));
331}
332
333
334int
335main(int argc, char *argv[])
336{
337	struct ptrace_lwpinfo lwpinfo;
338	int c, status, use_vfork;
339	pid_t pid, pid1;
340
341	trace_syscalls = 1;
342	use_vfork = 0;
343	while ((c = getopt(argc, argv, "csv")) != -1) {
344		switch (c) {
345		case 'c':
346			trace_syscalls = 0;
347			break;
348		case 's':
349			trace_syscalls = 1;
350			break;
351		case 'v':
352			use_vfork = 1;
353			break;
354		default:
355		case '?':
356			fprintf(stderr, "Usage: %s [-c] [-s] [-v]\n", argv[0]);
357			return (2);
358		}
359	}
360
361	if ((pid = fork()) < 0) {
362		perror("fork");
363		return 1;
364	}
365	else if (pid == 0) {
366		if (ptrace(PT_TRACE_ME, 0, NULL, 0) < 0) {
367			perror("PT_TRACE_ME");
368			_exit(1);
369		}
370		kill(getpid(), SIGSTOP);
371		getpid();
372		if ((pid1 = use_vfork ? vfork() : fork()) < 0) {
373			perror("fork1");
374			return (1);
375		} else if (pid1 == 0) {
376			printf("Hi from child %d\n", getpid());
377			execl("/bin/ls", "ls", "/", (char *)NULL);
378		}
379	}
380	else { /* parent */
381		if (waitpid(pid, &status, 0) == -1) {
382			perror("waitpid");
383			return (-1);
384		}
385		assert(WIFSTOPPED(status));
386		assert(WSTOPSIG(status) == SIGSTOP);
387
388		if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo,
389		    sizeof(lwpinfo)) < 0) {
390			perror("PT_LWPINFO");
391			ptrace(PT_KILL, pid, NULL, 0);
392			return (-1);
393		}
394		wait_info(pid, status, &lwpinfo);
395
396		if (ptrace(PT_FOLLOW_FORK, pid, 0, 1) < 0) {
397			perror("PT_FOLLOW_FORK");
398			ptrace(PT_KILL, pid, NULL, 0);
399			return (2);
400		}
401
402		while ((pid1 = trace(pid)) >= 0) {
403			if (pid1 != 0) {
404				printf(TRACE "attached to pid %d\n", pid1);
405#if 0
406				kill(pid1, SIGCONT);
407#endif
408				if (waitpid(pid1, &status, 0) == -1) {
409					perror("waitpid");
410					return (-1);
411				}
412				printf(TRACE "nested loop, pid %d status %s\n",
413				    pid1, decode_wait_status(status));
414				assert(WIFSTOPPED(status));
415				assert(WSTOPSIG(status) == SIGSTOP);
416				if (ptrace(PT_LWPINFO, pid1, (caddr_t)&lwpinfo,
417				    sizeof(lwpinfo)) < 0) {
418					perror("PT_LWPINFO");
419					ptrace(PT_KILL, pid1, NULL, 0);
420					return (-1);
421				}
422				wait_info(pid1, status, &lwpinfo);
423
424				while (trace(pid1) >= 0)
425					;
426			}
427		}
428
429		ptrace(PT_CONTINUE, pid, (caddr_t)1, 0);
430	}
431	return (0);
432}
433