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
41 static const char *
decode_wait_status(int status)42 decode_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
83 static const char *
decode_pl_flags(struct ptrace_lwpinfo * lwpinfo)84 decode_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
133 static const char *
decode_pl_event(struct ptrace_lwpinfo * lwpinfo)134 decode_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
149 static void
get_pathname(pid_t pid)150 get_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
179 static void
wait_info(int pid,int status,struct ptrace_lwpinfo * lwpinfo)180 wait_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
214 static int trace_syscalls = 1;
215 static int remote_getpid = 0;
216
217 static int
trace_sc(int pid)218 trace_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
302 static int
trace_cont(int pid)303 trace_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
345 static int
trace(pid_t pid)346 trace(pid_t pid)
347 {
348
349 return (trace_syscalls ? trace_sc(pid) : trace_cont(pid));
350 }
351
352
353 int
main(int argc,char * argv[])354 main(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