1 /*-
2 * Copyright (c) 2011, 2012 Konstantin Belousov <[email protected]>
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 #include <sys/types.h>
28 #include <sys/ptrace.h>
29 #include <sys/syscall.h>
30 #include <sys/sysctl.h>
31 #include <sys/wait.h>
32 #include <assert.h>
33 #include <errno.h>
34 #include <signal.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
39
40 #define TRACE ">>>> "
41
42 static const char *
decode_wait_status(int status)43 decode_wait_status(int status)
44 {
45 static char c[128];
46 char b[32];
47 int first;
48
49 c[0] = '\0';
50 first = 1;
51 if (WIFCONTINUED(status)) {
52 first = 0;
53 strlcat(c, "CONT", sizeof(c));
54 }
55 if (WIFEXITED(status)) {
56 if (first)
57 first = 0;
58 else
59 strlcat(c, ",", sizeof(c));
60 snprintf(b, sizeof(b), "EXIT(%d)", WEXITSTATUS(status));
61 strlcat(c, b, sizeof(c));
62 }
63 if (WIFSIGNALED(status)) {
64 if (first)
65 first = 0;
66 else
67 strlcat(c, ",", sizeof(c));
68 snprintf(b, sizeof(b), "SIG(%s)", strsignal(WTERMSIG(status)));
69 strlcat(c, b, sizeof(c));
70 if (WCOREDUMP(status))
71 strlcat(c, ",CORE", sizeof(c));
72 }
73 if (WIFSTOPPED(status)) {
74 if (first)
75 first = 0;
76 else
77 strlcat(c, ",", sizeof(c));
78 snprintf(b, sizeof(b), "SIG(%s)", strsignal(WSTOPSIG(status)));
79 strlcat(c, b, sizeof(c));
80 }
81 return (c);
82 }
83
84 static const char *
decode_pl_flags(struct ptrace_lwpinfo * lwpinfo)85 decode_pl_flags(struct ptrace_lwpinfo *lwpinfo)
86 {
87 static char c[128];
88 static struct decode_tag {
89 int flag;
90 const char *desc;
91 } decode[] = {
92 { PL_FLAG_SA, "SA" },
93 { PL_FLAG_BOUND, "BOUND" },
94 { PL_FLAG_SCE, "SCE" },
95 { PL_FLAG_SCX, "SCX" },
96 { PL_FLAG_EXEC, "EXEC" },
97 { PL_FLAG_SI, "SI" },
98 { PL_FLAG_FORKED, "FORKED" },
99 { PL_FLAG_CHILD, "CHILD" },
100 { PL_FLAG_BORN, "LWPBORN" },
101 { PL_FLAG_EXITED, "LWPEXITED" },
102 { PL_FLAG_VFORKED, "VFORKED" },
103 { PL_FLAG_VFORK_DONE, "VFORKDONE" },
104 };
105 char de[32];
106 unsigned first, flags, i;
107
108 c[0] = '\0';
109 first = 1;
110 flags = lwpinfo->pl_flags;
111 for (i = 0; i < sizeof(decode) / sizeof(decode[0]); i++) {
112 if ((flags & decode[i].flag) != 0) {
113 if (first)
114 first = 0;
115 else
116 strlcat(c, ",", sizeof(c));
117 strlcat(c, decode[i].desc, sizeof(c));
118 flags &= ~decode[i].flag;
119 }
120 }
121 for (i = 0; i < sizeof(flags) * NBBY; i++) {
122 if ((flags & (1 << i)) != 0) {
123 if (first)
124 first = 0;
125 else
126 strlcat(c, ",", sizeof(c));
127 snprintf(de, sizeof(de), "<%d>", i);
128 strlcat(c, de, sizeof(c));
129 }
130 }
131 return (c);
132 }
133
134 static const char *
decode_pl_event(struct ptrace_lwpinfo * lwpinfo)135 decode_pl_event(struct ptrace_lwpinfo *lwpinfo)
136 {
137
138 switch (lwpinfo->pl_event) {
139 case PL_EVENT_NONE:
140 return ("NONE");
141
142 case PL_EVENT_SIGNAL:
143 return ("SIG");
144
145 default:
146 return ("UNKNOWN");
147 }
148 }
149
150 static void
get_pathname(pid_t pid)151 get_pathname(pid_t pid)
152 {
153 char pathname[PATH_MAX];
154 int error, name[4];
155 size_t len;
156
157 name[0] = CTL_KERN;
158 name[1] = KERN_PROC;
159 name[2] = KERN_PROC_PATHNAME;
160 name[3] = pid;
161
162 len = sizeof(pathname);
163 error = sysctl(name, 4, pathname, &len, NULL, 0);
164 if (error < 0) {
165 if (errno != ESRCH) {
166 fprintf(stderr, "sysctl kern.proc.pathname.%d: %s\n",
167 pid, strerror(errno));
168 return;
169 }
170 fprintf(stderr, "pid %d exited\n", pid);
171 return;
172 }
173 if (len == 0 || strlen(pathname) == 0) {
174 fprintf(stderr, "No cached pathname for process %d\n", pid);
175 return;
176 }
177 printf(TRACE "pid %d path %s\n", pid, pathname);
178 }
179
180 static void
wait_info(int pid,int status,struct ptrace_lwpinfo * lwpinfo)181 wait_info(int pid, int status, struct ptrace_lwpinfo *lwpinfo)
182 {
183 long *args;
184 int error, i;
185
186 printf(TRACE "pid %d wait %s", pid,
187 decode_wait_status(status));
188 if (lwpinfo != NULL) {
189 printf(" event %s flags %s",
190 decode_pl_event(lwpinfo), decode_pl_flags(lwpinfo));
191 if ((lwpinfo->pl_flags & (PL_FLAG_SCE | PL_FLAG_SCX)) != 0) {
192 printf(" sc%d", lwpinfo->pl_syscall_code);
193 args = calloc(lwpinfo->pl_syscall_narg, sizeof(long));
194 error = ptrace(PT_GET_SC_ARGS, lwpinfo->pl_lwpid,
195 (caddr_t)args, lwpinfo->pl_syscall_narg *
196 sizeof(long));
197 if (error == 0) {
198 printf("(");
199 for (i = 0; i < (int)lwpinfo->pl_syscall_narg;
200 i++) {
201 printf("%s%#lx", i == 0 ? "" : ",",
202 args[i]);
203 }
204 printf(")");
205 } else {
206 fprintf(stderr, "PT_GET_SC_ARGS failed: %s",
207 strerror(errno));
208 }
209 free(args);
210 }
211 }
212 printf("\n");
213 }
214
215 static int trace_syscalls = 1;
216 static int remote_getpid = 0;
217
218 static int
trace_sc(int pid)219 trace_sc(int pid)
220 {
221 struct ptrace_sc_remote pscr;
222 struct ptrace_lwpinfo lwpinfo;
223 int status;
224
225 if (ptrace(PT_TO_SCE, pid, (caddr_t)1, 0) < 0) {
226 perror("PT_TO_SCE");
227 ptrace(PT_KILL, pid, NULL, 0);
228 return (-1);
229 }
230
231 if (waitpid(pid, &status, 0) == -1) {
232 perror("waitpid");
233 return (-1);
234 }
235 if (WIFEXITED(status) || WIFSIGNALED(status)) {
236 wait_info(pid, status, NULL);
237 return (-1);
238 }
239 assert(WIFSTOPPED(status));
240 assert(WSTOPSIG(status) == SIGTRAP);
241
242 if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) {
243 perror("PT_LWPINFO");
244 ptrace(PT_KILL, pid, NULL, 0);
245 return (-1);
246 }
247 wait_info(pid, status, &lwpinfo);
248 assert(lwpinfo.pl_flags & PL_FLAG_SCE);
249
250 if (ptrace(PT_TO_SCX, pid, (caddr_t)1, 0) < 0) {
251 perror("PT_TO_SCX");
252 ptrace(PT_KILL, pid, NULL, 0);
253 return (-1);
254 }
255
256 if (waitpid(pid, &status, 0) == -1) {
257 perror("waitpid");
258 return (-1);
259 }
260 if (WIFEXITED(status) || WIFSIGNALED(status)) {
261 wait_info(pid, status, NULL);
262 return (-1);
263 }
264 assert(WIFSTOPPED(status));
265 assert(WSTOPSIG(status) == SIGTRAP);
266
267 if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) {
268 perror("PT_LWPINFO");
269 ptrace(PT_KILL, pid, NULL, 0);
270 return (-1);
271 }
272 wait_info(pid, status, &lwpinfo);
273 assert(lwpinfo.pl_flags & PL_FLAG_SCX);
274
275 if (remote_getpid) {
276 memset(&pscr, 0, sizeof(pscr));
277 pscr.pscr_syscall = SYS_getpid;
278 pscr.pscr_nargs = 0;
279 if (ptrace(PT_SC_REMOTE, pid, (caddr_t)&pscr,
280 sizeof(pscr)) < 0) {
281 perror("PT_SC_REMOTE");
282 ptrace(PT_KILL, pid, NULL, 0);
283 return (-1);
284 } else {
285 printf(TRACE "remote getpid %ld errno %d\n",
286 pscr.pscr_ret.sr_retval[0], pscr.pscr_ret.sr_error);
287 if (waitpid(pid, &status, 0) == -1) {
288 perror("waitpid");
289 return (-1);
290 }
291 }
292 }
293 if (lwpinfo.pl_flags & PL_FLAG_EXEC)
294 get_pathname(pid);
295
296 if (lwpinfo.pl_flags & PL_FLAG_FORKED) {
297 printf(TRACE "forked child %d\n", lwpinfo.pl_child_pid);
298 return (lwpinfo.pl_child_pid);
299 }
300 return (0);
301 }
302
303 static int
trace_cont(int pid)304 trace_cont(int pid)
305 {
306 struct ptrace_lwpinfo lwpinfo;
307 int status;
308
309 if (ptrace(PT_CONTINUE, pid, (caddr_t)1, 0) < 0) {
310 perror("PT_CONTINUE");
311 ptrace(PT_KILL, pid, NULL, 0);
312 return (-1);
313 }
314
315 if (waitpid(pid, &status, 0) == -1) {
316 perror("waitpid");
317 return (-1);
318 }
319 if (WIFEXITED(status) || WIFSIGNALED(status)) {
320 wait_info(pid, status, NULL);
321 return (-1);
322 }
323 assert(WIFSTOPPED(status));
324 assert(WSTOPSIG(status) == SIGTRAP);
325
326 if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) {
327 perror("PT_LWPINFO");
328 ptrace(PT_KILL, pid, NULL, 0);
329 return (-1);
330 }
331 wait_info(pid, status, &lwpinfo);
332
333 if ((lwpinfo.pl_flags & (PL_FLAG_EXEC | PL_FLAG_SCX)) ==
334 (PL_FLAG_EXEC | PL_FLAG_SCX))
335 get_pathname(pid);
336
337 if ((lwpinfo.pl_flags & (PL_FLAG_FORKED | PL_FLAG_SCX)) ==
338 (PL_FLAG_FORKED | PL_FLAG_SCX)) {
339 printf(TRACE "forked child %d\n", lwpinfo.pl_child_pid);
340 return (lwpinfo.pl_child_pid);
341 }
342
343 return (0);
344 }
345
346 static int
trace(pid_t pid)347 trace(pid_t pid)
348 {
349
350 return (trace_syscalls ? trace_sc(pid) : trace_cont(pid));
351 }
352
353
354 int
main(int argc,char * argv[])355 main(int argc, char *argv[])
356 {
357 struct ptrace_lwpinfo lwpinfo;
358 int c, status, use_vfork;
359 pid_t pid, pid1;
360
361 trace_syscalls = 1;
362 remote_getpid = 0;
363 use_vfork = 0;
364 while ((c = getopt(argc, argv, "crsv")) != -1) {
365 switch (c) {
366 case 'c':
367 trace_syscalls = 0;
368 break;
369 case 'r':
370 remote_getpid = 1;
371 break;
372 case 's':
373 trace_syscalls = 1;
374 break;
375 case 'v':
376 use_vfork = 1;
377 break;
378 default:
379 case '?':
380 fprintf(stderr, "Usage: %s [-c] [-r] [-s] [-v]\n",
381 argv[0]);
382 return (2);
383 }
384 }
385
386 if ((pid = fork()) < 0) {
387 perror("fork");
388 return 1;
389 }
390 else if (pid == 0) {
391 if (ptrace(PT_TRACE_ME, 0, NULL, 0) < 0) {
392 perror("PT_TRACE_ME");
393 _exit(1);
394 }
395 kill(getpid(), SIGSTOP);
396 getpid();
397 if ((pid1 = use_vfork ? vfork() : fork()) < 0) {
398 perror("fork1");
399 return (1);
400 } else if (pid1 == 0) {
401 printf("Hi from child %d\n", getpid());
402 execl("/bin/ls", "ls", "/", (char *)NULL);
403 }
404 }
405 else { /* parent */
406 if (waitpid(pid, &status, 0) == -1) {
407 perror("waitpid");
408 return (-1);
409 }
410 assert(WIFSTOPPED(status));
411 assert(WSTOPSIG(status) == SIGSTOP);
412
413 if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo,
414 sizeof(lwpinfo)) < 0) {
415 perror("PT_LWPINFO");
416 ptrace(PT_KILL, pid, NULL, 0);
417 return (-1);
418 }
419 wait_info(pid, status, &lwpinfo);
420
421 if (ptrace(PT_FOLLOW_FORK, pid, 0, 1) < 0) {
422 perror("PT_FOLLOW_FORK");
423 ptrace(PT_KILL, pid, NULL, 0);
424 return (2);
425 }
426
427 while ((pid1 = trace(pid)) >= 0) {
428 if (pid1 != 0) {
429 printf(TRACE "attached to pid %d\n", pid1);
430 #if 0
431 kill(pid1, SIGCONT);
432 #endif
433 if (waitpid(pid1, &status, 0) == -1) {
434 perror("waitpid");
435 return (-1);
436 }
437 printf(TRACE "nested loop, pid %d status %s\n",
438 pid1, decode_wait_status(status));
439 assert(WIFSTOPPED(status));
440 assert(WSTOPSIG(status) == SIGSTOP);
441 if (ptrace(PT_LWPINFO, pid1, (caddr_t)&lwpinfo,
442 sizeof(lwpinfo)) < 0) {
443 perror("PT_LWPINFO");
444 ptrace(PT_KILL, pid1, NULL, 0);
445 return (-1);
446 }
447 wait_info(pid1, status, &lwpinfo);
448
449 while (trace(pid1) >= 0)
450 ;
451 }
452 }
453
454 ptrace(PT_CONTINUE, pid, (caddr_t)1, 0);
455 }
456 return (0);
457 }
458