2 * Copyright (c) 2011, 2012 Konstantin Belousov <kib@FreeBSD.org>
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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
26 #include <sys/cdefs.h>
27 __FBSDID("$FreeBSD$");
29 #include <sys/types.h>
30 #include <sys/ptrace.h>
31 #include <sys/sysctl.h>
44 decode_wait_status(int status)
52 if (WIFCONTINUED(status)) {
54 strlcat(c, "CONT", sizeof(c));
56 if (WIFEXITED(status)) {
60 strlcat(c, ",", sizeof(c));
61 snprintf(b, sizeof(b), "EXIT(%d)", WEXITSTATUS(status));
62 strlcat(c, b, sizeof(c));
64 if (WIFSIGNALED(status)) {
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));
74 if (WIFSTOPPED(status)) {
78 strlcat(c, ",", sizeof(c));
79 snprintf(b, sizeof(b), "SIG(%s)", strsignal(WSTOPSIG(status)));
80 strlcat(c, b, sizeof(c));
86 decode_pl_flags(struct ptrace_lwpinfo *lwpinfo)
89 static struct decode_tag {
94 { PL_FLAG_BOUND, "BOUND" },
95 { PL_FLAG_SCE, "SCE" },
96 { PL_FLAG_SCX, "SCX" },
97 { PL_FLAG_EXEC, "EXEC" },
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" },
107 unsigned first, flags, i;
111 flags = lwpinfo->pl_flags;
112 for (i = 0; i < sizeof(decode) / sizeof(decode[0]); i++) {
113 if ((flags & decode[i].flag) != 0) {
117 strlcat(c, ",", sizeof(c));
118 strlcat(c, decode[i].desc, sizeof(c));
119 flags &= ~decode[i].flag;
122 for (i = 0; i < sizeof(flags) * NBBY; i++) {
123 if ((flags & (1 << i)) != 0) {
127 strlcat(c, ",", sizeof(c));
128 snprintf(de, sizeof(de), "<%d>", i);
129 strlcat(c, de, sizeof(c));
136 decode_pl_event(struct ptrace_lwpinfo *lwpinfo)
139 switch (lwpinfo->pl_event) {
143 case PL_EVENT_SIGNAL:
152 get_pathname(pid_t pid)
154 char pathname[PATH_MAX];
160 name[2] = KERN_PROC_PATHNAME;
163 len = sizeof(pathname);
164 error = sysctl(name, 4, pathname, &len, NULL, 0);
166 if (errno != ESRCH) {
167 fprintf(stderr, "sysctl kern.proc.pathname.%d: %s\n",
168 pid, strerror(errno));
171 fprintf(stderr, "pid %d exited\n", pid);
174 if (len == 0 || strlen(pathname) == 0) {
175 fprintf(stderr, "No cached pathname for process %d\n", pid);
178 printf(TRACE "pid %d path %s\n", pid, pathname);
182 wait_info(int pid, int status, struct ptrace_lwpinfo *lwpinfo)
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 *
199 for (i = 0; i < (int)lwpinfo->pl_syscall_narg;
201 printf("%c%#lx", i == 0 ? '(' : ',',
205 fprintf(stderr, "PT_GET_SC_ARGS failed: %s",
218 struct ptrace_lwpinfo lwpinfo;
221 if (ptrace(PT_TO_SCE, pid, (caddr_t)1, 0) < 0) {
223 ptrace(PT_KILL, pid, NULL, 0);
227 if (waitpid(pid, &status, 0) == -1) {
231 if (WIFEXITED(status) || WIFSIGNALED(status)) {
232 wait_info(pid, status, NULL);
235 assert(WIFSTOPPED(status));
236 assert(WSTOPSIG(status) == SIGTRAP);
238 if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) {
239 perror("PT_LWPINFO");
240 ptrace(PT_KILL, pid, NULL, 0);
243 wait_info(pid, status, &lwpinfo);
244 assert(lwpinfo.pl_flags & PL_FLAG_SCE);
246 if (ptrace(PT_TO_SCX, pid, (caddr_t)1, 0) < 0) {
248 ptrace(PT_KILL, pid, NULL, 0);
252 if (waitpid(pid, &status, 0) == -1) {
256 if (WIFEXITED(status) || WIFSIGNALED(status)) {
257 wait_info(pid, status, NULL);
260 assert(WIFSTOPPED(status));
261 assert(WSTOPSIG(status) == SIGTRAP);
263 if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) {
264 perror("PT_LWPINFO");
265 ptrace(PT_KILL, pid, NULL, 0);
268 wait_info(pid, status, &lwpinfo);
269 assert(lwpinfo.pl_flags & PL_FLAG_SCX);
271 if (lwpinfo.pl_flags & PL_FLAG_EXEC)
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);
284 struct ptrace_lwpinfo lwpinfo;
287 if (ptrace(PT_CONTINUE, pid, (caddr_t)1, 0) < 0) {
288 perror("PT_CONTINUE");
289 ptrace(PT_KILL, pid, NULL, 0);
293 if (waitpid(pid, &status, 0) == -1) {
297 if (WIFEXITED(status) || WIFSIGNALED(status)) {
298 wait_info(pid, status, NULL);
301 assert(WIFSTOPPED(status));
302 assert(WSTOPSIG(status) == SIGTRAP);
304 if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) {
305 perror("PT_LWPINFO");
306 ptrace(PT_KILL, pid, NULL, 0);
309 wait_info(pid, status, &lwpinfo);
311 if ((lwpinfo.pl_flags & (PL_FLAG_EXEC | PL_FLAG_SCX)) ==
312 (PL_FLAG_EXEC | PL_FLAG_SCX))
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);
324 static int trace_syscalls = 1;
330 return (trace_syscalls ? trace_sc(pid) : trace_cont(pid));
335 main(int argc, char *argv[])
337 struct ptrace_lwpinfo lwpinfo;
338 int c, status, use_vfork;
343 while ((c = getopt(argc, argv, "csv")) != -1) {
356 fprintf(stderr, "Usage: %s [-c] [-s] [-v]\n", argv[0]);
361 if ((pid = fork()) < 0) {
366 if (ptrace(PT_TRACE_ME, 0, NULL, 0) < 0) {
367 perror("PT_TRACE_ME");
370 kill(getpid(), SIGSTOP);
372 if ((pid1 = use_vfork ? vfork() : fork()) < 0) {
375 } else if (pid1 == 0) {
376 printf("Hi from child %d\n", getpid());
377 execl("/bin/ls", "ls", "/", (char *)NULL);
381 if (waitpid(pid, &status, 0) == -1) {
385 assert(WIFSTOPPED(status));
386 assert(WSTOPSIG(status) == SIGSTOP);
388 if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo,
389 sizeof(lwpinfo)) < 0) {
390 perror("PT_LWPINFO");
391 ptrace(PT_KILL, pid, NULL, 0);
394 wait_info(pid, status, &lwpinfo);
396 if (ptrace(PT_FOLLOW_FORK, pid, 0, 1) < 0) {
397 perror("PT_FOLLOW_FORK");
398 ptrace(PT_KILL, pid, NULL, 0);
402 while ((pid1 = trace(pid)) >= 0) {
404 printf(TRACE "attached to pid %d\n", pid1);
408 if (waitpid(pid1, &status, 0) == -1) {
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);
422 wait_info(pid1, status, &lwpinfo);
424 while (trace(pid1) >= 0)
429 ptrace(PT_CONTINUE, pid, (caddr_t)1, 0);