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$");
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
43 static const char *
decode_wait_status(int status)44 decode_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
85 static const char *
decode_pl_flags(struct ptrace_lwpinfo * lwpinfo)86 decode_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
130 static const char *
decode_pl_event(struct ptrace_lwpinfo * lwpinfo)131 decode_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
146 static void
get_pathname(pid_t pid)147 get_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
176 static void
wait_info(int pid,int status,struct ptrace_lwpinfo * lwpinfo)177 wait_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
189 static int
trace_sc(int pid)190 trace_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
255 static int
trace_cont(int pid)256 trace_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
298 static int trace_syscalls = 1;
299
300 static int
trace(pid_t pid)301 trace(pid_t pid)
302 {
303
304 return (trace_syscalls ? trace_sc(pid) : trace_cont(pid));
305 }
306
307
308 int
main(int argc,char * argv[])309 main(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