]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tools/test/ptrace/scescx.c
test/ptrace/scescx.c: fix printing of braces for syscalls without args
[FreeBSD/FreeBSD.git] / tools / test / ptrace / scescx.c
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 *
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 *
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                 { 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
135 static const char *
136 decode_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
151 static void
152 get_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
181 static void
182 wait_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                                 printf("(");
200                                 for (i = 0; i < (int)lwpinfo->pl_syscall_narg;
201                                     i++) {
202                                         printf("%s%#lx", i == 0 ? "" : ",",
203                                             args[i]);
204                                 }
205                                 printf(")");
206                         } else {
207                                 fprintf(stderr, "PT_GET_SC_ARGS failed: %s",
208                                     strerror(errno));
209                         }
210                         free(args);
211                 }
212         }
213         printf("\n");
214 }
215
216 static int
217 trace_sc(int pid)
218 {
219         struct ptrace_lwpinfo lwpinfo;
220         int status;
221
222         if (ptrace(PT_TO_SCE, pid, (caddr_t)1, 0) < 0) {
223                 perror("PT_TO_SCE");
224                 ptrace(PT_KILL, pid, NULL, 0);
225                 return (-1);
226         }
227
228         if (waitpid(pid, &status, 0) == -1) {
229                 perror("waitpid");
230                 return (-1);
231         }
232         if (WIFEXITED(status) || WIFSIGNALED(status)) {
233                 wait_info(pid, status, NULL);
234                 return (-1);
235         }
236         assert(WIFSTOPPED(status));
237         assert(WSTOPSIG(status) == SIGTRAP);
238
239         if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) {
240                 perror("PT_LWPINFO");
241                 ptrace(PT_KILL, pid, NULL, 0);
242                 return (-1);
243         }
244         wait_info(pid, status, &lwpinfo);
245         assert(lwpinfo.pl_flags & PL_FLAG_SCE);
246
247         if (ptrace(PT_TO_SCX, pid, (caddr_t)1, 0) < 0) {
248                 perror("PT_TO_SCX");
249                 ptrace(PT_KILL, pid, NULL, 0);
250                 return (-1);
251         }
252
253         if (waitpid(pid, &status, 0) == -1) {
254                 perror("waitpid");
255                 return (-1);
256         }
257         if (WIFEXITED(status) || WIFSIGNALED(status)) {
258                 wait_info(pid, status, NULL);
259                 return (-1);
260         }
261         assert(WIFSTOPPED(status));
262         assert(WSTOPSIG(status) == SIGTRAP);
263
264         if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) {
265                 perror("PT_LWPINFO");
266                 ptrace(PT_KILL, pid, NULL, 0);
267                 return (-1);
268         }
269         wait_info(pid, status, &lwpinfo);
270         assert(lwpinfo.pl_flags & PL_FLAG_SCX);
271
272         if (lwpinfo.pl_flags & PL_FLAG_EXEC)
273                 get_pathname(pid);
274
275         if (lwpinfo.pl_flags & PL_FLAG_FORKED) {
276                 printf(TRACE "forked child %d\n", lwpinfo.pl_child_pid);
277                 return (lwpinfo.pl_child_pid);
278         }
279         return (0);
280 }
281
282 static int
283 trace_cont(int pid)
284 {
285         struct ptrace_lwpinfo lwpinfo;
286         int status;
287
288         if (ptrace(PT_CONTINUE, pid, (caddr_t)1, 0) < 0) {
289                 perror("PT_CONTINUE");
290                 ptrace(PT_KILL, pid, NULL, 0);
291                 return (-1);
292         }
293
294         if (waitpid(pid, &status, 0) == -1) {
295                 perror("waitpid");
296                 return (-1);
297         }
298         if (WIFEXITED(status) || WIFSIGNALED(status)) {
299                 wait_info(pid, status, NULL);
300                 return (-1);
301         }
302         assert(WIFSTOPPED(status));
303         assert(WSTOPSIG(status) == SIGTRAP);
304
305         if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) {
306                 perror("PT_LWPINFO");
307                 ptrace(PT_KILL, pid, NULL, 0);
308                 return (-1);
309         }
310         wait_info(pid, status, &lwpinfo);
311
312         if ((lwpinfo.pl_flags & (PL_FLAG_EXEC | PL_FLAG_SCX)) ==
313             (PL_FLAG_EXEC | PL_FLAG_SCX))
314                 get_pathname(pid);
315
316         if ((lwpinfo.pl_flags & (PL_FLAG_FORKED | PL_FLAG_SCX)) ==
317             (PL_FLAG_FORKED | PL_FLAG_SCX)) {
318                 printf(TRACE "forked child %d\n", lwpinfo.pl_child_pid);
319                 return (lwpinfo.pl_child_pid);
320         }
321
322         return (0);
323 }
324
325 static int trace_syscalls = 1;
326
327 static int
328 trace(pid_t pid)
329 {
330
331         return (trace_syscalls ? trace_sc(pid) : trace_cont(pid));
332 }
333
334
335 int
336 main(int argc, char *argv[])
337 {
338         struct ptrace_lwpinfo lwpinfo;
339         int c, status, use_vfork;
340         pid_t pid, pid1;
341
342         trace_syscalls = 1;
343         use_vfork = 0;
344         while ((c = getopt(argc, argv, "csv")) != -1) {
345                 switch (c) {
346                 case 'c':
347                         trace_syscalls = 0;
348                         break;
349                 case 's':
350                         trace_syscalls = 1;
351                         break;
352                 case 'v':
353                         use_vfork = 1;
354                         break;
355                 default:
356                 case '?':
357                         fprintf(stderr, "Usage: %s [-c] [-s] [-v]\n", argv[0]);
358                         return (2);
359                 }
360         }
361
362         if ((pid = fork()) < 0) {
363                 perror("fork");
364                 return 1;
365         }
366         else if (pid == 0) {
367                 if (ptrace(PT_TRACE_ME, 0, NULL, 0) < 0) {
368                         perror("PT_TRACE_ME");
369                         _exit(1);
370                 }
371                 kill(getpid(), SIGSTOP);
372                 getpid();
373                 if ((pid1 = use_vfork ? vfork() : fork()) < 0) {
374                         perror("fork1");
375                         return (1);
376                 } else if (pid1 == 0) {
377                         printf("Hi from child %d\n", getpid());
378                         execl("/bin/ls", "ls", "/", (char *)NULL);
379                 }
380         }
381         else { /* parent */
382                 if (waitpid(pid, &status, 0) == -1) {
383                         perror("waitpid");
384                         return (-1);
385                 }
386                 assert(WIFSTOPPED(status));
387                 assert(WSTOPSIG(status) == SIGSTOP);
388
389                 if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo,
390                     sizeof(lwpinfo)) < 0) {
391                         perror("PT_LWPINFO");
392                         ptrace(PT_KILL, pid, NULL, 0);
393                         return (-1);
394                 }
395                 wait_info(pid, status, &lwpinfo);
396
397                 if (ptrace(PT_FOLLOW_FORK, pid, 0, 1) < 0) {
398                         perror("PT_FOLLOW_FORK");
399                         ptrace(PT_KILL, pid, NULL, 0);
400                         return (2);
401                 }
402
403                 while ((pid1 = trace(pid)) >= 0) {
404                         if (pid1 != 0) {
405                                 printf(TRACE "attached to pid %d\n", pid1);
406 #if 0
407                                 kill(pid1, SIGCONT);
408 #endif
409                                 if (waitpid(pid1, &status, 0) == -1) {
410                                         perror("waitpid");
411                                         return (-1);
412                                 }
413                                 printf(TRACE "nested loop, pid %d status %s\n",
414                                     pid1, decode_wait_status(status));
415                                 assert(WIFSTOPPED(status));
416                                 assert(WSTOPSIG(status) == SIGSTOP);
417                                 if (ptrace(PT_LWPINFO, pid1, (caddr_t)&lwpinfo,
418                                     sizeof(lwpinfo)) < 0) {
419                                         perror("PT_LWPINFO");
420                                         ptrace(PT_KILL, pid1, NULL, 0);
421                                         return (-1);
422                                 }
423                                 wait_info(pid1, status, &lwpinfo);
424
425                                 while (trace(pid1) >= 0)
426                                         ;
427                         }
428                 }
429
430                 ptrace(PT_CONTINUE, pid, (caddr_t)1, 0);
431         }
432         return (0);
433 }