]> CyberLeo.Net >> Repos - FreeBSD/stable/9.git/blob - usr.bin/truss/syscalls.c
MFC 253471,253620,254430,254538:
[FreeBSD/stable/9.git] / usr.bin / truss / syscalls.c
1 /*
2  * Copyright 1997 Sean Eric Fagan
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  * 3. All advertising materials mentioning features or use of this software
13  *    must display the following acknowledgement:
14  *      This product includes software developed by Sean Eric Fagan
15  * 4. Neither the name of the author may be used to endorse or promote
16  *    products derived from this software without specific prior written
17  *    permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31
32 #ifndef lint
33 static const char rcsid[] =
34   "$FreeBSD$";
35 #endif /* not lint */
36
37 /*
38  * This file has routines used to print out system calls and their
39  * arguments.
40  */
41
42 #include <sys/mman.h>
43 #include <sys/types.h>
44 #include <sys/ptrace.h>
45 #include <sys/socket.h>
46 #include <sys/time.h>
47 #include <sys/un.h>
48 #include <netinet/in.h>
49 #include <arpa/inet.h>
50 #include <sys/ioccom.h>
51 #include <machine/atomic.h>
52 #include <errno.h>
53 #include <sys/umtx.h>
54 #include <sys/event.h>
55 #include <sys/stat.h>
56 #include <sys/resource.h>
57
58 #include <ctype.h>
59 #include <err.h>
60 #include <fcntl.h>
61 #include <poll.h>
62 #include <signal.h>
63 #include <stdint.h>
64 #include <stdio.h>
65 #include <stdlib.h>
66 #include <string.h>
67 #include <time.h>
68 #include <unistd.h>
69 #include <vis.h>
70
71 #include "truss.h"
72 #include "extern.h"
73 #include "syscall.h"
74
75 /* 64-bit alignment on 32-bit platforms. */
76 #ifdef __powerpc__
77 #define QUAD_ALIGN      1
78 #else
79 #define QUAD_ALIGN      0
80 #endif
81
82 /* Number of slots needed for a 64-bit argument. */
83 #ifdef __LP64__
84 #define QUAD_SLOTS      1
85 #else
86 #define QUAD_SLOTS      2
87 #endif
88
89 /*
90  * This should probably be in its own file, sorted alphabetically.
91  */
92 struct syscall syscalls[] = {
93         { .name = "fcntl", .ret_type = 1, .nargs = 3,
94           .args = { { Int, 0 } , { Fcntl, 1 }, { Fcntlflag | OUT, 2 } } },
95         { .name = "fork", .ret_type = 1, .nargs = 0 },
96         { .name = "vfork", .ret_type = 1, .nargs = 0 },
97         { .name = "rfork", .ret_type = 1, .nargs = 1,
98           .args = { { Rforkflags, 0 } } },
99         { .name = "getegid", .ret_type = 1, .nargs = 0 },
100         { .name = "geteuid", .ret_type = 1, .nargs = 0 },
101         { .name = "getgid", .ret_type = 1, .nargs = 0 },
102         { .name = "getpid", .ret_type = 1, .nargs = 0 },
103         { .name = "getpgid", .ret_type = 1, .nargs = 1,
104           .args = { { Int, 0 } } },
105         { .name = "getpgrp", .ret_type = 1, .nargs = 0 },
106         { .name = "getppid", .ret_type = 1, .nargs = 0 },
107         { .name = "getsid", .ret_type = 1, .nargs = 1,
108           .args = { { Int, 0 } } },
109         { .name = "getuid", .ret_type = 1, .nargs = 0 },
110         { .name = "readlink", .ret_type = 1, .nargs = 3,
111           .args = { { Name, 0 } , { Readlinkres | OUT, 1 }, { Int, 2 } } },
112         { .name = "lseek", .ret_type = 2, .nargs = 3,
113           .args = { { Int, 0 }, { Quad, 1 + QUAD_ALIGN }, { Whence, 1 + QUAD_SLOTS + QUAD_ALIGN } } },
114         { .name = "linux_lseek", .ret_type = 2, .nargs = 3,
115           .args = { { Int, 0 }, { Int, 1 }, { Whence, 2 } } },
116         { .name = "mmap", .ret_type = 2, .nargs = 6,
117           .args = { { Ptr, 0 }, { Int, 1 }, { Mprot, 2 }, { Mmapflags, 3 }, { Int, 4 }, { Quad, 5 + QUAD_ALIGN } } },
118         { .name = "mprotect", .ret_type = 1, .nargs = 3,
119           .args = { { Ptr, 0 }, { Int, 1 }, { Mprot, 2 } } },
120         { .name = "open", .ret_type = 1, .nargs = 3,
121           .args = { { Name | IN, 0 } , { Open, 1 }, { Octal, 2 } } },
122         { .name = "mkdir", .ret_type = 1, .nargs = 2,
123           .args = { { Name, 0 } , { Octal, 1 } } },
124         { .name = "linux_open", .ret_type = 1, .nargs = 3,
125           .args = { { Name, 0 }, { Hex, 1 }, { Octal, 2 } } },
126         { .name = "close", .ret_type = 1, .nargs = 1,
127           .args = { { Int, 0 } } },
128         { .name = "link", .ret_type = 0, .nargs = 2,
129           .args = { { Name, 0 }, { Name, 1 } } },
130         { .name = "unlink", .ret_type = 0, .nargs = 1,
131           .args = { { Name, 0 } } },
132         { .name = "chdir", .ret_type = 0, .nargs = 1,
133           .args = { { Name, 0 } } },
134         { .name = "chroot", .ret_type = 0, .nargs = 1,
135           .args = { { Name, 0 } } },
136         { .name = "mknod", .ret_type = 0, .nargs = 3,
137           .args = { { Name, 0 }, { Octal, 1 }, { Int, 3 } } },
138         { .name = "chmod", .ret_type = 0, .nargs = 2,
139           .args = { { Name, 0 }, { Octal, 1 } } },
140         { .name = "chown", .ret_type = 0, .nargs = 3,
141           .args = { { Name, 0 }, { Int, 1 }, { Int, 2 } } },
142         { .name = "mount", .ret_type = 0, .nargs = 4,
143           .args = { { Name, 0 }, { Name, 1 }, { Int, 2 }, { Ptr, 3 } } },
144         { .name = "umount", .ret_type = 0, .nargs = 2,
145           .args = { { Name, 0 }, { Int, 2 } } },
146         { .name = "fstat", .ret_type = 1, .nargs = 2,
147           .args = { { Int, 0 }, { Stat | OUT , 1 } } },
148         { .name = "stat", .ret_type = 1, .nargs = 2,
149           .args = { { Name | IN, 0 }, { Stat | OUT, 1 } } },
150         { .name = "lstat", .ret_type = 1, .nargs = 2,
151           .args = { { Name | IN, 0 }, { Stat | OUT, 1 } } },
152         { .name = "linux_newstat", .ret_type = 1, .nargs = 2,
153           .args = { { Name | IN, 0 }, { Ptr | OUT, 1 } } },
154         { .name = "linux_newfstat", .ret_type = 1, .nargs = 2,
155           .args = { { Int, 0 }, { Ptr | OUT, 1 } } },
156         { .name = "write", .ret_type = 1, .nargs = 3,
157           .args = { { Int, 0 }, { BinString | IN, 1 }, { Int, 2 } } },
158         { .name = "ioctl", .ret_type = 1, .nargs = 3,
159           .args = { { Int, 0 }, { Ioctl, 1 }, { Hex, 2 } } },
160         { .name = "break", .ret_type = 1, .nargs = 1,
161           .args = { { Ptr, 0 } } },
162         { .name = "exit", .ret_type = 0, .nargs = 1,
163           .args = { { Hex, 0 } } },
164         { .name = "access", .ret_type = 1, .nargs = 2,
165           .args = { { Name | IN, 0 }, { Int, 1 } } },
166         { .name = "sigaction", .ret_type = 1, .nargs = 3,
167           .args = { { Signal, 0 }, { Sigaction | IN, 1 }, { Sigaction | OUT, 2 } } },
168         { .name = "accept", .ret_type = 1, .nargs = 3,
169           .args = { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } },
170         { .name = "bind", .ret_type = 1, .nargs = 3,
171           .args = { { Int, 0 }, { Sockaddr | IN, 1 }, { Int, 2 } } },
172         { .name = "connect", .ret_type = 1, .nargs = 3,
173           .args = { { Int, 0 }, { Sockaddr | IN, 1 }, { Int, 2 } } },
174         { .name = "getpeername", .ret_type = 1, .nargs = 3,
175           .args = { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } },
176         { .name = "getsockname", .ret_type = 1, .nargs = 3,
177           .args = { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } },
178         { .name = "recvfrom", .ret_type = 1, .nargs = 6,
179           .args = { { Int, 0 }, { BinString | OUT, 1 }, { Int, 2 }, { Hex, 3 }, { Sockaddr | OUT, 4 }, { Ptr | OUT, 5 } } },
180         { .name = "sendto", .ret_type = 1, .nargs = 6,
181           .args = { { Int, 0 }, { BinString | IN, 1 }, { Int, 2 }, { Hex, 3 }, { Sockaddr | IN, 4 }, { Ptr | IN, 5 } } },
182         { .name = "execve", .ret_type = 1, .nargs = 3,
183           .args = { { Name | IN, 0 }, { StringArray | IN, 1 }, { StringArray | IN, 2 } } },
184         { .name = "linux_execve", .ret_type = 1, .nargs = 3,
185           .args = { { Name | IN, 0 }, { StringArray | IN, 1 }, { StringArray | IN, 2 } } },
186         { .name = "kldload", .ret_type = 0, .nargs = 1,
187           .args = { { Name | IN, 0 } } },
188         { .name = "kldunload", .ret_type = 0, .nargs = 1,
189           .args = { { Int, 0 } } },
190         { .name = "kldfind", .ret_type = 0, .nargs = 1,
191           .args = { { Name | IN, 0 } } },
192         { .name = "kldnext", .ret_type = 0, .nargs = 1,
193           .args = { { Int, 0 } } },
194         { .name = "kldstat", .ret_type = 0, .nargs = 2,
195           .args = { { Int, 0 }, { Ptr, 1 } } },
196         { .name = "kldfirstmod", .ret_type = 0, .nargs = 1,
197           .args = { { Int, 0 } } },
198         { .name = "nanosleep", .ret_type = 0, .nargs = 1,
199           .args = { { Timespec, 0 } } },
200         { .name = "select", .ret_type = 1, .nargs = 5,
201           .args = { { Int, 0 }, { Fd_set, 1 }, { Fd_set, 2 }, { Fd_set, 3 }, { Timeval, 4 } } },
202         { .name = "poll", .ret_type = 1, .nargs = 3,
203           .args = { { Pollfd, 0 }, { Int, 1 }, { Int, 2 } } },
204         { .name = "gettimeofday", .ret_type = 1, .nargs = 2,
205           .args = { { Timeval | OUT, 0 }, { Ptr, 1 } } },
206         { .name = "clock_gettime", .ret_type = 1, .nargs = 2,
207           .args = { { Int, 0 }, { Timespec | OUT, 1 } } },
208         { .name = "getitimer", .ret_type = 1, .nargs = 2,
209           .args = { { Int, 0 }, { Itimerval | OUT, 2 } } },
210         { .name = "setitimer", .ret_type = 1, .nargs = 3,
211           .args = { { Int, 0 }, { Itimerval, 1 } , { Itimerval | OUT, 2 } } },
212         { .name = "kse_release", .ret_type = 0, .nargs = 1,
213           .args = { { Timespec, 0 } } },
214         { .name = "kevent", .ret_type = 0, .nargs = 6,
215           .args = { { Int, 0 }, { Kevent, 1 }, { Int, 2 }, { Kevent | OUT, 3 }, { Int, 4 }, { Timespec, 5 } } },
216         { .name = "_umtx_lock", .ret_type = 0, .nargs = 1,
217           .args = { { Umtx, 0 } } },
218         { .name = "_umtx_unlock", .ret_type = 0, .nargs = 1,
219           .args = { { Umtx, 0 } } },
220         { .name = "sigprocmask", .ret_type = 0, .nargs = 3,
221           .args = { { Sigprocmask, 0 }, { Sigset, 1 }, { Sigset | OUT, 2 } } },
222         { .name = "unmount", .ret_type = 1, .nargs = 2,
223           .args = { { Name, 0 }, { Int, 1 } } },
224         { .name = "socket", .ret_type = 1, .nargs = 3,
225           .args = { { Sockdomain, 0 }, { Socktype, 1 }, { Int, 2 } } },
226         { .name = "getrusage", .ret_type = 1, .nargs = 2,
227           .args = { { Int, 0 }, { Rusage | OUT, 1 } } },
228         { .name = "__getcwd", .ret_type = 1, .nargs = 2,
229           .args = { { Name | OUT, 0 }, { Int, 1 } } },
230         { .name = "shutdown", .ret_type = 1, .nargs = 2,
231           .args = { { Int, 0 }, { Shutdown, 1 } } },
232         { .name = "getrlimit", .ret_type = 1, .nargs = 2,
233           .args = { { Resource, 0 }, { Rlimit | OUT, 1 } } },
234         { .name = "setrlimit", .ret_type = 1, .nargs = 2,
235           .args = { { Resource, 0 }, { Rlimit | IN, 1 } } },
236         { .name = "utimes", .ret_type = 1, .nargs = 2,
237           .args = { { Name | IN, 0 }, { Timeval2 | IN, 1 } } },
238         { .name = "lutimes", .ret_type = 1, .nargs = 2,
239           .args = { { Name | IN, 0 }, { Timeval2 | IN, 1 } } },
240         { .name = "futimes", .ret_type = 1, .nargs = 2,
241           .args = { { Int, 0 }, { Timeval | IN, 1 } } },
242         { .name = "chflags", .ret_type = 1, .nargs = 2,
243           .args = { { Name | IN, 0 }, { Hex, 1 } } },
244         { .name = "lchflags", .ret_type = 1, .nargs = 2,
245           .args = { { Name | IN, 0 }, { Hex, 1 } } },
246         { .name = "pathconf", .ret_type = 1, .nargs = 2,
247           .args = { { Name | IN, 0 }, { Pathconf, 1 } } },
248         { .name = "pipe", .ret_type = 1, .nargs = 1,
249           .args = { { Ptr, 0 } } },
250         { .name = "truncate", .ret_type = 1, .nargs = 3,
251           .args = { { Name | IN, 0 }, { Int | IN, 1 }, { Quad | IN, 2 } } },
252         { .name = "ftruncate", .ret_type = 1, .nargs = 3,
253           .args = { { Int | IN, 0 }, { Int | IN, 1 }, { Quad | IN, 2 } } },
254         { .name = "kill", .ret_type = 1, .nargs = 2,
255           .args = { { Int | IN, 0 }, { Signal | IN, 1 } } },
256         { .name = "munmap", .ret_type = 1, .nargs = 2,
257           .args = { { Ptr, 0 }, { Int, 1 } } },
258         { .name = "read", .ret_type = 1, .nargs = 3,
259           .args = { { Int, 0 }, { BinString | OUT, 1 }, { Int, 2 } } },
260         { .name = "rename", .ret_type = 1, .nargs = 2,
261           .args = { { Name , 0 } , { Name, 1 } } },
262         { .name = "symlink", .ret_type = 1, .nargs = 2,
263           .args = { { Name , 0 } , { Name, 1 } } },
264         { .name = "posix_openpt", .ret_type = 1, .nargs = 1,
265           .args = { { Open, 0 } } },
266         { .name = 0 },
267 };
268
269 /* Xlat idea taken from strace */
270 struct xlat {
271         int val;
272         const char *str;
273 };
274
275 #define X(a)    { a, #a },
276 #define XEND    { 0, NULL }
277
278 static struct xlat kevent_filters[] = {
279         X(EVFILT_READ) X(EVFILT_WRITE) X(EVFILT_AIO) X(EVFILT_VNODE)
280         X(EVFILT_PROC) X(EVFILT_SIGNAL) X(EVFILT_TIMER)
281         X(EVFILT_FS) X(EVFILT_READ) XEND
282 };
283
284 static struct xlat kevent_flags[] = {
285         X(EV_ADD) X(EV_DELETE) X(EV_ENABLE) X(EV_DISABLE) X(EV_ONESHOT)
286         X(EV_CLEAR) X(EV_FLAG1) X(EV_ERROR) X(EV_EOF) XEND
287 };
288
289 struct xlat poll_flags[] = {
290         X(POLLSTANDARD) X(POLLIN) X(POLLPRI) X(POLLOUT) X(POLLERR)
291         X(POLLHUP) X(POLLNVAL) X(POLLRDNORM) X(POLLRDBAND)
292         X(POLLWRBAND) X(POLLINIGNEOF) XEND
293 };
294
295 static struct xlat mmap_flags[] = {
296         X(MAP_SHARED) X(MAP_PRIVATE) X(MAP_FIXED) X(MAP_RENAME)
297         X(MAP_NORESERVE) X(MAP_RESERVED0080) X(MAP_RESERVED0100)
298         X(MAP_HASSEMAPHORE) X(MAP_STACK) X(MAP_NOSYNC) X(MAP_ANON)
299         X(MAP_NOCORE) X(MAP_PREFAULT_READ) XEND
300 };
301
302 static struct xlat mprot_flags[] = {
303         X(PROT_NONE) X(PROT_READ) X(PROT_WRITE) X(PROT_EXEC) XEND
304 };
305
306 static struct xlat whence_arg[] = {
307         X(SEEK_SET) X(SEEK_CUR) X(SEEK_END) XEND
308 };
309
310 static struct xlat sigaction_flags[] = {
311         X(SA_ONSTACK) X(SA_RESTART) X(SA_RESETHAND) X(SA_NOCLDSTOP)
312         X(SA_NODEFER) X(SA_NOCLDWAIT) X(SA_SIGINFO) XEND
313 };
314
315 static struct xlat fcntl_arg[] = {
316         X(F_DUPFD) X(F_GETFD) X(F_SETFD) X(F_GETFL) X(F_SETFL)
317         X(F_GETOWN) X(F_SETOWN) X(F_GETLK) X(F_SETLK) X(F_SETLKW) XEND
318 };
319
320 static struct xlat fcntlfd_arg[] = {
321         X(FD_CLOEXEC) XEND
322 };
323
324 static struct xlat fcntlfl_arg[] = {
325         X(O_APPEND) X(O_ASYNC) X(O_FSYNC) X(O_NONBLOCK) X(O_NOFOLLOW)
326         X(O_DIRECT) XEND
327 };
328
329 static struct xlat sockdomain_arg[] = {
330         X(PF_UNSPEC) X(PF_LOCAL) X(PF_UNIX) X(PF_INET) X(PF_IMPLINK)
331         X(PF_PUP) X(PF_CHAOS) X(PF_NETBIOS) X(PF_ISO) X(PF_OSI)
332         X(PF_ECMA) X(PF_DATAKIT) X(PF_CCITT) X(PF_SNA) X(PF_DECnet)
333         X(PF_DLI) X(PF_LAT) X(PF_HYLINK) X(PF_APPLETALK) X(PF_ROUTE)
334         X(PF_LINK) X(PF_XTP) X(PF_COIP) X(PF_CNT) X(PF_SIP) X(PF_IPX)
335         X(PF_RTIP) X(PF_PIP) X(PF_ISDN) X(PF_KEY) X(PF_INET6)
336         X(PF_NATM) X(PF_ATM) X(PF_NETGRAPH) X(PF_SLOW) X(PF_SCLUSTER)
337         X(PF_ARP) X(PF_BLUETOOTH) XEND
338 };
339
340 static struct xlat socktype_arg[] = {
341         X(SOCK_STREAM) X(SOCK_DGRAM) X(SOCK_RAW) X(SOCK_RDM)
342         X(SOCK_SEQPACKET) XEND
343 };
344
345 static struct xlat open_flags[] = {
346         X(O_RDONLY) X(O_WRONLY) X(O_RDWR) X(O_ACCMODE) X(O_NONBLOCK)
347         X(O_APPEND) X(O_SHLOCK) X(O_EXLOCK) X(O_ASYNC) X(O_FSYNC)
348         X(O_NOFOLLOW) X(O_CREAT) X(O_TRUNC) X(O_EXCL) X(O_NOCTTY)
349         X(O_DIRECT) X(O_DIRECTORY) X(O_EXEC) X(O_TTY_INIT) X(O_CLOEXEC) XEND
350 };
351
352 static struct xlat shutdown_arg[] = {
353         X(SHUT_RD) X(SHUT_WR) X(SHUT_RDWR) XEND
354 };
355
356 static struct xlat resource_arg[] = {
357         X(RLIMIT_CPU) X(RLIMIT_FSIZE) X(RLIMIT_DATA) X(RLIMIT_STACK)
358         X(RLIMIT_CORE) X(RLIMIT_RSS) X(RLIMIT_MEMLOCK) X(RLIMIT_NPROC)
359         X(RLIMIT_NOFILE) X(RLIMIT_SBSIZE) X(RLIMIT_VMEM) XEND
360 };
361
362 static struct xlat pathconf_arg[] = {
363         X(_PC_LINK_MAX)  X(_PC_MAX_CANON)  X(_PC_MAX_INPUT)
364         X(_PC_NAME_MAX) X(_PC_PATH_MAX) X(_PC_PIPE_BUF)
365         X(_PC_CHOWN_RESTRICTED) X(_PC_NO_TRUNC) X(_PC_VDISABLE)
366         X(_PC_ASYNC_IO) X(_PC_PRIO_IO) X(_PC_SYNC_IO)
367         X(_PC_ALLOC_SIZE_MIN) X(_PC_FILESIZEBITS)
368         X(_PC_REC_INCR_XFER_SIZE) X(_PC_REC_MAX_XFER_SIZE)
369         X(_PC_REC_MIN_XFER_SIZE) X(_PC_REC_XFER_ALIGN)
370         X(_PC_SYMLINK_MAX) X(_PC_ACL_EXTENDED) X(_PC_ACL_PATH_MAX)
371         X(_PC_CAP_PRESENT) X(_PC_INF_PRESENT) X(_PC_MAC_PRESENT)
372         XEND
373 };
374
375 static struct xlat rfork_flags[] = {
376         X(RFPROC) X(RFNOWAIT) X(RFFDG) X(RFCFDG) X(RFTHREAD) X(RFMEM)
377         X(RFSIGSHARE) X(RFTSIGZMB) X(RFLINUXTHPN) XEND
378 };
379
380 #undef X
381 #undef XEND
382
383 /*
384  * Searches an xlat array for a value, and returns it if found.  Otherwise
385  * return a string representation.
386  */
387 static const char *
388 lookup(struct xlat *xlat, int val, int base)
389 {
390         static char tmp[16];
391
392         for (; xlat->str != NULL; xlat++)
393                 if (xlat->val == val)
394                         return (xlat->str);
395         switch (base) {
396                 case 8:
397                         sprintf(tmp, "0%o", val);
398                         break;
399                 case 16:
400                         sprintf(tmp, "0x%x", val);
401                         break;
402                 case 10:
403                         sprintf(tmp, "%u", val);
404                         break;
405                 default:
406                         errx(1,"Unknown lookup base");
407                         break;
408         }
409         return (tmp);
410 }
411
412 static const char *
413 xlookup(struct xlat *xlat, int val)
414 {
415
416         return (lookup(xlat, val, 16));
417 }
418
419 /* Searches an xlat array containing bitfield values.  Remaining bits
420    set after removing the known ones are printed at the end:
421    IN|0x400 */
422 static char *
423 xlookup_bits(struct xlat *xlat, int val)
424 {
425         int len, rem;
426         static char str[512];
427
428         len = 0;
429         rem = val;
430         for (; xlat->str != NULL; xlat++) {
431                 if ((xlat->val & rem) == xlat->val) {
432                         /* don't print the "all-bits-zero" string unless all
433                            bits are really zero */
434                         if (xlat->val == 0 && val != 0)
435                                 continue;
436                         len += sprintf(str + len, "%s|", xlat->str);
437                         rem &= ~(xlat->val);
438                 }
439         }
440         /* if we have leftover bits or didn't match anything */
441         if (rem || len == 0)
442                 len += sprintf(str + len, "0x%x", rem);
443         if (len && str[len - 1] == '|')
444                 len--;
445         str[len] = 0;
446         return (str);
447 }
448
449 /*
450  * If/when the list gets big, it might be desirable to do it
451  * as a hash table or binary search.
452  */
453
454 struct syscall *
455 get_syscall(const char *name)
456 {
457         struct syscall *sc;
458
459         sc = syscalls;
460         if (name == NULL)
461                 return (NULL);
462         while (sc->name) {
463                 if (strcmp(name, sc->name) == 0)
464                         return (sc);
465                 sc++;
466         }
467         return (NULL);
468 }
469
470 /*
471  * get_struct
472  *
473  * Copy a fixed amount of bytes from the process.
474  */
475
476 static int
477 get_struct(pid_t pid, void *offset, void *buf, int len)
478 {
479         struct ptrace_io_desc iorequest;
480
481         iorequest.piod_op = PIOD_READ_D;
482         iorequest.piod_offs = offset;
483         iorequest.piod_addr = buf;
484         iorequest.piod_len = len;
485         if (ptrace(PT_IO, pid, (caddr_t)&iorequest, 0) < 0)
486                 return (-1);
487         return (0);
488 }
489
490 #define MAXSIZE         4096
491 #define BLOCKSIZE       1024
492 /*
493  * get_string
494  * Copy a string from the process.  Note that it is
495  * expected to be a C string, but if max is set, it will
496  * only get that much.
497  */
498
499 static char *
500 get_string(pid_t pid, void *offset, int max)
501 {
502         struct ptrace_io_desc iorequest;
503         char *buf;
504         int diff, i, size, totalsize;
505
506         diff = 0;
507         totalsize = size = max ? (max + 1) : BLOCKSIZE;
508         buf = malloc(totalsize);
509         if (buf == NULL)
510                 return (NULL);
511         for (;;) {
512                 diff = totalsize - size;
513                 iorequest.piod_op = PIOD_READ_D;
514                 iorequest.piod_offs = (char *)offset + diff;
515                 iorequest.piod_addr = buf + diff;
516                 iorequest.piod_len = size;
517                 if (ptrace(PT_IO, pid, (caddr_t)&iorequest, 0) < 0) {
518                         free(buf);
519                         return (NULL);
520                 }
521                 for (i = 0 ; i < size; i++) {
522                         if (buf[diff + i] == '\0')
523                                 return (buf);
524                 }
525                 if (totalsize < MAXSIZE - BLOCKSIZE && max == 0) {
526                         totalsize += BLOCKSIZE;
527                         buf = realloc(buf, totalsize);
528                         size = BLOCKSIZE;
529                 } else {
530                         buf[totalsize - 1] = '\0';
531                         return (buf);
532                 }
533         }
534 }
535
536
537 /*
538  * print_arg
539  * Converts a syscall argument into a string.  Said string is
540  * allocated via malloc(), so needs to be free()'d.  The file
541  * descriptor is for the process' memory (via /proc), and is used
542  * to get any data (where the argument is a pointer).  sc is
543  * a pointer to the syscall description (see above); args is
544  * an array of all of the system call arguments.
545  */
546
547 char *
548 print_arg(struct syscall_args *sc, unsigned long *args, long retval,
549     struct trussinfo *trussinfo)
550 {
551         char *tmp;
552         pid_t pid;
553
554         tmp = NULL;
555         pid = trussinfo->pid;
556         switch (sc->type & ARG_MASK) {
557         case Hex:
558                 asprintf(&tmp, "0x%x", (int)args[sc->offset]);
559                 break;
560         case Octal:
561                 asprintf(&tmp, "0%o", (int)args[sc->offset]);
562                 break;
563         case Int:
564                 asprintf(&tmp, "%d", (int)args[sc->offset]);
565                 break;
566         case Name: {
567                 /* NULL-terminated string. */
568                 char *tmp2;
569                 tmp2 = get_string(pid, (void*)args[sc->offset], 0);
570                 asprintf(&tmp, "\"%s\"", tmp2);
571                 free(tmp2);
572                 break;
573         }
574         case BinString: {
575                 /* Binary block of data that might have printable characters.
576                    XXX If type|OUT, assume that the length is the syscall's
577                    return value.  Otherwise, assume that the length of the block
578                    is in the next syscall argument. */
579                 int max_string = trussinfo->strsize;
580                 char tmp2[max_string+1], *tmp3;
581                 int len;
582                 int truncated = 0;
583
584                 if (sc->type & OUT)
585                         len = retval;
586                 else
587                         len = args[sc->offset + 1];
588
589                 /* Don't print more than max_string characters, to avoid word
590                    wrap.  If we have to truncate put some ... after the string.
591                 */
592                 if (len > max_string) {
593                         len = max_string;
594                         truncated = 1;
595                 }
596                 if (len && get_struct(pid, (void*)args[sc->offset], &tmp2, len)
597                     != -1) {
598                         tmp3 = malloc(len * 4 + 1);
599                         while (len) {
600                                 if (strvisx(tmp3, tmp2, len,
601                                     VIS_CSTYLE|VIS_TAB|VIS_NL) <= max_string)
602                                         break;
603                                 len--;
604                                 truncated = 1;
605                         };
606                         asprintf(&tmp, "\"%s\"%s", tmp3, truncated ?
607                             "..." : "");
608                         free(tmp3);
609                 } else {
610                         asprintf(&tmp, "0x%lx", args[sc->offset]);
611                 }
612                 break;
613         }
614         case StringArray: {
615                 int num, size, i;
616                 char *tmp2;
617                 char *string;
618                 char *strarray[100];    /* XXX This is ugly. */
619
620                 if (get_struct(pid, (void *)args[sc->offset],
621                     (void *)&strarray, sizeof(strarray)) == -1)
622                         err(1, "get_struct %p", (void *)args[sc->offset]);
623                 num = 0;
624                 size = 0;
625
626                 /* Find out how large of a buffer we'll need. */
627                 while (strarray[num] != NULL) {
628                         string = get_string(pid, (void*)strarray[num], 0);
629                         size += strlen(string);
630                         free(string);
631                         num++;
632                 }
633                 size += 4 + (num * 4);
634                 tmp = (char *)malloc(size);
635                 tmp2 = tmp;
636
637                 tmp2 += sprintf(tmp2, " [");
638                 for (i = 0; i < num; i++) {
639                         string = get_string(pid, (void*)strarray[i], 0);
640                         tmp2 += sprintf(tmp2, " \"%s\"%c", string,
641                             (i + 1 == num) ? ' ' : ',');
642                         free(string);
643                 }
644                 tmp2 += sprintf(tmp2, "]");
645                 break;
646         }
647 #ifdef __LP64__
648         case Quad:
649                 asprintf(&tmp, "0x%lx", args[sc->offset]);
650                 break;
651 #else
652         case Quad: {
653                 unsigned long long ll;
654                 ll = *(unsigned long long *)(args + sc->offset);
655                 asprintf(&tmp, "0x%llx", ll);
656                 break;
657         }
658 #endif
659         case Ptr:
660                 asprintf(&tmp, "0x%lx", args[sc->offset]);
661                 break;
662         case Readlinkres: {
663                 char *tmp2;
664                 if (retval == -1) {
665                         tmp = strdup("");
666                         break;
667                 }
668                 tmp2 = get_string(pid, (void*)args[sc->offset], retval);
669                 asprintf(&tmp, "\"%s\"", tmp2);
670                 free(tmp2);
671                 break;
672         }
673         case Ioctl: {
674                 const char *temp = ioctlname(args[sc->offset]);
675                 if (temp)
676                         tmp = strdup(temp);
677                 else {
678                         unsigned long arg = args[sc->offset];
679                         asprintf(&tmp, "0x%lx { IO%s%s 0x%lx('%c'), %lu, %lu }",
680                             arg, arg & IOC_OUT ? "R" : "",
681                             arg & IOC_IN ? "W" : "", IOCGROUP(arg),
682                             isprint(IOCGROUP(arg)) ? (char)IOCGROUP(arg) : '?',
683                             arg & 0xFF, IOCPARM_LEN(arg));
684                 }
685                 break;
686         }
687         case Umtx: {
688                 struct umtx umtx;
689                 if (get_struct(pid, (void *)args[sc->offset], &umtx,
690                     sizeof(umtx)) != -1)
691                         asprintf(&tmp, "{ 0x%lx }", (long)umtx.u_owner);
692                 else
693                         asprintf(&tmp, "0x%lx", args[sc->offset]);
694                 break;
695         }
696         case Timespec: {
697                 struct timespec ts;
698                 if (get_struct(pid, (void *)args[sc->offset], &ts,
699                     sizeof(ts)) != -1)
700                         asprintf(&tmp, "{%ld.%09ld }", (long)ts.tv_sec,
701                             ts.tv_nsec);
702                 else
703                         asprintf(&tmp, "0x%lx", args[sc->offset]);
704                 break;
705         }
706         case Timeval: {
707                 struct timeval tv;
708                 if (get_struct(pid, (void *)args[sc->offset], &tv, sizeof(tv))
709                     != -1)
710                         asprintf(&tmp, "{%ld.%06ld }", (long)tv.tv_sec,
711                             tv.tv_usec);
712                 else
713                         asprintf(&tmp, "0x%lx", args[sc->offset]);
714                 break;
715         }
716         case Timeval2: {
717                 struct timeval tv[2];
718                 if (get_struct(pid, (void *)args[sc->offset], &tv, sizeof(tv))
719                     != -1)
720                         asprintf(&tmp, "{%ld.%06ld, %ld.%06ld }",
721                             (long)tv[0].tv_sec, tv[0].tv_usec,
722                             (long)tv[1].tv_sec, tv[1].tv_usec);
723                 else
724                         asprintf(&tmp, "0x%lx", args[sc->offset]);
725                 break;
726         }
727         case Itimerval: {
728                 struct itimerval itv;
729                 if (get_struct(pid, (void *)args[sc->offset], &itv,
730                     sizeof(itv)) != -1)
731                         asprintf(&tmp, "{%ld.%06ld, %ld.%06ld }",
732                             (long)itv.it_interval.tv_sec,
733                             itv.it_interval.tv_usec,
734                             (long)itv.it_value.tv_sec,
735                             itv.it_value.tv_usec);
736                 else
737                         asprintf(&tmp, "0x%lx", args[sc->offset]);
738                 break;
739         }
740         case Pollfd: {
741                 /*
742                  * XXX: A Pollfd argument expects the /next/ syscall argument
743                  * to be the number of fds in the array. This matches the poll
744                  * syscall.
745                  */
746                 struct pollfd *pfd;
747                 int numfds = args[sc->offset+1];
748                 int bytes = sizeof(struct pollfd) * numfds;
749                 int i, tmpsize, u, used;
750                 const int per_fd = 100;
751
752                 if ((pfd = malloc(bytes)) == NULL)
753                         err(1, "Cannot malloc %d bytes for pollfd array",
754                             bytes);
755                 if (get_struct(pid, (void *)args[sc->offset], pfd, bytes)
756                     != -1) {
757                         used = 0;
758                         tmpsize = 1 + per_fd * numfds + 2;
759                         if ((tmp = malloc(tmpsize)) == NULL)
760                                 err(1, "Cannot alloc %d bytes for poll output",
761                                     tmpsize);
762
763                         tmp[used++] = '{';
764                         for (i = 0; i < numfds; i++) {
765
766                                 u = snprintf(tmp + used, per_fd, "%s%d/%s",
767                                     i > 0 ? " " : "", pfd[i].fd,
768                                     xlookup_bits(poll_flags, pfd[i].events));
769                                 if (u > 0)
770                                         used += u < per_fd ? u : per_fd;
771                         }
772                         tmp[used++] = '}';
773                         tmp[used++] = '\0';
774                 } else {
775                         asprintf(&tmp, "0x%lx", args[sc->offset]);
776                 }
777                 free(pfd);
778                 break;
779         }
780         case Fd_set: {
781                 /*
782                  * XXX: A Fd_set argument expects the /first/ syscall argument
783                  * to be the number of fds in the array.  This matches the
784                  * select syscall.
785                  */
786                 fd_set *fds;
787                 int numfds = args[0];
788                 int bytes = _howmany(numfds, _NFDBITS) * _NFDBITS;
789                 int i, tmpsize, u, used;
790                 const int per_fd = 20;
791
792                 if ((fds = malloc(bytes)) == NULL)
793                         err(1, "Cannot malloc %d bytes for fd_set array",
794                             bytes);
795                 if (get_struct(pid, (void *)args[sc->offset], fds, bytes)
796                     != -1) {
797                         used = 0;
798                         tmpsize = 1 + numfds * per_fd + 2;
799                         if ((tmp = malloc(tmpsize)) == NULL)
800                                 err(1, "Cannot alloc %d bytes for fd_set "
801                                     "output", tmpsize);
802
803                         tmp[used++] = '{';
804                         for (i = 0; i < numfds; i++) {
805                                 if (FD_ISSET(i, fds)) {
806                                         u = snprintf(tmp + used, per_fd, "%d ",
807                                             i);
808                                         if (u > 0)
809                                                 used += u < per_fd ? u : per_fd;
810                                 }
811                         }
812                         if (tmp[used-1] == ' ')
813                                 used--;
814                         tmp[used++] = '}';
815                         tmp[used++] = '\0';
816                 } else
817                         asprintf(&tmp, "0x%lx", args[sc->offset]);
818                 free(fds);
819                 break;
820         }
821         case Signal: {
822                 long sig;
823
824                 sig = args[sc->offset];
825                 tmp = strsig(sig);
826                 if (tmp == NULL)
827                         asprintf(&tmp, "%ld", sig);
828                 break;
829         }
830         case Sigset: {
831                 long sig;
832                 sigset_t ss;
833                 int i, used;
834
835                 sig = args[sc->offset];
836                 if (get_struct(pid, (void *)args[sc->offset], (void *)&ss,
837                     sizeof(ss)) == -1) {
838                         asprintf(&tmp, "0x%lx", args[sc->offset]);
839                         break;
840                 }
841                 tmp = malloc(sys_nsig * 8); /* 7 bytes avg per signal name */
842                 used = 0;
843                 for (i = 1; i < sys_nsig; i++) {
844                         if (sigismember(&ss, i))
845                                 used += sprintf(tmp + used, "%s|", strsig(i));
846                 }
847                 if (used)
848                         tmp[used-1] = 0;
849                 else
850                         strcpy(tmp, "0x0");
851                 break;
852         }
853         case Sigprocmask: {
854                 switch (args[sc->offset]) {
855 #define S(a)    case a: tmp = strdup(#a); break;
856                         S(SIG_BLOCK);
857                         S(SIG_UNBLOCK);
858                         S(SIG_SETMASK);
859 #undef S
860                 }
861                 if (tmp == NULL)
862                         asprintf(&tmp, "0x%lx", args[sc->offset]);
863                 break;
864         }
865         case Fcntlflag: {
866                 /* XXX output depends on the value of the previous argument */
867                 switch (args[sc->offset-1]) {
868                 case F_SETFD:
869                         tmp = strdup(xlookup_bits(fcntlfd_arg,
870                             args[sc->offset]));
871                         break;
872                 case F_SETFL:
873                         tmp = strdup(xlookup_bits(fcntlfl_arg,
874                             args[sc->offset]));
875                         break;
876                 case F_GETFD:
877                 case F_GETFL:
878                 case F_GETOWN:
879                         tmp = strdup("");
880                         break;
881                 default:
882                         asprintf(&tmp, "0x%lx", args[sc->offset]);
883                         break;
884                 }
885                 break;
886         }
887         case Open:
888                 tmp = strdup(xlookup_bits(open_flags, args[sc->offset]));
889                 break;
890         case Fcntl:
891                 tmp = strdup(xlookup(fcntl_arg, args[sc->offset]));
892                 break;
893         case Mprot:
894                 tmp = strdup(xlookup_bits(mprot_flags, args[sc->offset]));
895                 break;
896         case Mmapflags: {
897                 char *base, *alignstr;
898                 int align, flags;
899
900                 /*
901                  * MAP_ALIGNED can't be handled by xlookup_bits(), so
902                  * generate that string manually and prepend it to the
903                  * string from xlookup_bits().  Have to be careful to
904                  * avoid outputting MAP_ALIGNED|0 if MAP_ALIGNED is
905                  * the only flag.
906                  */
907                 flags = args[sc->offset] & ~MAP_ALIGNMENT_MASK;
908                 align = args[sc->offset] & MAP_ALIGNMENT_MASK;
909                 if (align != 0) {
910                         if (align == MAP_ALIGNED_SUPER)
911                                 alignstr = strdup("MAP_ALIGNED_SUPER");
912                         else
913                                 asprintf(&alignstr, "MAP_ALIGNED(%d)",
914                                     align >> MAP_ALIGNMENT_SHIFT);
915                         if (flags == 0) {
916                                 tmp = alignstr;
917                                 break;
918                         }
919                 } else
920                         alignstr = NULL;
921                 base = strdup(xlookup_bits(mmap_flags, flags));
922                 if (alignstr == NULL) {
923                         tmp = base;
924                         break;
925                 }
926                 asprintf(&tmp, "%s|%s", alignstr, base);
927                 free(alignstr);
928                 free(base);
929                 break;
930         }
931         case Whence:
932                 tmp = strdup(xlookup(whence_arg, args[sc->offset]));
933                 break;
934         case Sockdomain:
935                 tmp = strdup(xlookup(sockdomain_arg, args[sc->offset]));
936                 break;
937         case Socktype:
938                 tmp = strdup(xlookup(socktype_arg, args[sc->offset]));
939                 break;
940         case Shutdown:
941                 tmp = strdup(xlookup(shutdown_arg, args[sc->offset]));
942                 break;
943         case Resource:
944                 tmp = strdup(xlookup(resource_arg, args[sc->offset]));
945                 break;
946         case Pathconf:
947                 tmp = strdup(xlookup(pathconf_arg, args[sc->offset]));
948                 break;
949         case Rforkflags:
950                 tmp = strdup(xlookup_bits(rfork_flags, args[sc->offset]));
951                 break;
952         case Sockaddr: {
953                 struct sockaddr_storage ss;
954                 char addr[64];
955                 struct sockaddr_in *lsin;
956                 struct sockaddr_in6 *lsin6;
957                 struct sockaddr_un *sun;
958                 struct sockaddr *sa;
959                 char *p;
960                 u_char *q;
961                 int i;
962
963                 if (args[sc->offset] == 0) {
964                         asprintf(&tmp, "NULL");
965                         break;
966                 }
967
968                 /* yuck: get ss_len */
969                 if (get_struct(pid, (void *)args[sc->offset], (void *)&ss,
970                     sizeof(ss.ss_len) + sizeof(ss.ss_family)) == -1)
971                         err(1, "get_struct %p", (void *)args[sc->offset]);
972                 /*
973                  * If ss_len is 0, then try to guess from the sockaddr type.
974                  * AF_UNIX may be initialized incorrectly, so always frob
975                  * it by using the "right" size.
976                  */
977                 if (ss.ss_len == 0 || ss.ss_family == AF_UNIX) {
978                         switch (ss.ss_family) {
979                         case AF_INET:
980                                 ss.ss_len = sizeof(*lsin);
981                                 break;
982                         case AF_UNIX:
983                                 ss.ss_len = sizeof(*sun);
984                                 break;
985                         default:
986                                 /* hurrrr */
987                                 break;
988                         }
989                 }
990                 if (get_struct(pid, (void *)args[sc->offset], (void *)&ss,
991                     ss.ss_len) == -1) {
992                         err(2, "get_struct %p", (void *)args[sc->offset]);
993                 }
994
995                 switch (ss.ss_family) {
996                 case AF_INET:
997                         lsin = (struct sockaddr_in *)&ss;
998                         inet_ntop(AF_INET, &lsin->sin_addr, addr, sizeof addr);
999                         asprintf(&tmp, "{ AF_INET %s:%d }", addr,
1000                             htons(lsin->sin_port));
1001                         break;
1002                 case AF_INET6:
1003                         lsin6 = (struct sockaddr_in6 *)&ss;
1004                         inet_ntop(AF_INET6, &lsin6->sin6_addr, addr,
1005                             sizeof addr);
1006                         asprintf(&tmp, "{ AF_INET6 [%s]:%d }", addr,
1007                             htons(lsin6->sin6_port));
1008                         break;
1009                 case AF_UNIX:
1010                         sun = (struct sockaddr_un *)&ss;
1011                         asprintf(&tmp, "{ AF_UNIX \"%s\" }", sun->sun_path);
1012                         break;
1013                 default:
1014                         sa = (struct sockaddr *)&ss;
1015                         asprintf(&tmp, "{ sa_len = %d, sa_family = %d, sa_data "
1016                             "= {%n%*s } }", (int)sa->sa_len, (int)sa->sa_family,
1017                             &i, 6 * (int)(sa->sa_len - ((char *)&sa->sa_data -
1018                             (char *)sa)), "");
1019                         if (tmp != NULL) {
1020                                 p = tmp + i;
1021                                 for (q = (u_char *)&sa->sa_data;
1022                                     q < (u_char *)sa + sa->sa_len; q++)
1023                                         p += sprintf(p, " %#02x,", *q);
1024                         }
1025                 }
1026                 break;
1027         }
1028         case Sigaction: {
1029                 struct sigaction sa;
1030                 char *hand;
1031                 const char *h;
1032
1033                 if (get_struct(pid, (void *)args[sc->offset], &sa, sizeof(sa))
1034                     != -1) {
1035                         asprintf(&hand, "%p", sa.sa_handler);
1036                         if (sa.sa_handler == SIG_DFL)
1037                                 h = "SIG_DFL";
1038                         else if (sa.sa_handler == SIG_IGN)
1039                                 h = "SIG_IGN";
1040                         else
1041                                 h = hand;
1042
1043                         asprintf(&tmp, "{ %s %s ss_t }", h,
1044                             xlookup_bits(sigaction_flags, sa.sa_flags));
1045                         free(hand);
1046                 } else
1047                         asprintf(&tmp, "0x%lx", args[sc->offset]);
1048                 break;
1049         }
1050         case Kevent: {
1051                 /*
1052                  * XXX XXX: the size of the array is determined by either the
1053                  * next syscall argument, or by the syscall returnvalue,
1054                  * depending on which argument number we are.  This matches the
1055                  * kevent syscall, but luckily that's the only syscall that uses
1056                  * them.
1057                  */
1058                 struct kevent *ke;
1059                 int numevents = -1;
1060                 int bytes = 0;
1061                 int i, tmpsize, u, used;
1062                 const int per_ke = 100;
1063
1064                 if (sc->offset == 1)
1065                         numevents = args[sc->offset+1];
1066                 else if (sc->offset == 3 && retval != -1)
1067                         numevents = retval;
1068
1069                 if (numevents >= 0)
1070                         bytes = sizeof(struct kevent) * numevents;
1071                 if ((ke = malloc(bytes)) == NULL)
1072                         err(1, "Cannot malloc %d bytes for kevent array",
1073                             bytes);
1074                 if (numevents >= 0 && get_struct(pid, (void *)args[sc->offset],
1075                     ke, bytes) != -1) {
1076                         used = 0;
1077                         tmpsize = 1 + per_ke * numevents + 2;
1078                         if ((tmp = malloc(tmpsize)) == NULL)
1079                                 err(1, "Cannot alloc %d bytes for kevent "
1080                                     "output", tmpsize);
1081
1082                         tmp[used++] = '{';
1083                         for (i = 0; i < numevents; i++) {
1084                                 u = snprintf(tmp + used, per_ke,
1085                                     "%s%p,%s,%s,%d,%p,%p",
1086                                     i > 0 ? " " : "",
1087                                     (void *)ke[i].ident,
1088                                     xlookup(kevent_filters, ke[i].filter),
1089                                     xlookup_bits(kevent_flags, ke[i].flags),
1090                                     ke[i].fflags,
1091                                     (void *)ke[i].data,
1092                                     (void *)ke[i].udata);
1093                                 if (u > 0)
1094                                         used += u < per_ke ? u : per_ke;
1095                         }
1096                         tmp[used++] = '}';
1097                         tmp[used++] = '\0';
1098                 } else {
1099                         asprintf(&tmp, "0x%lx", args[sc->offset]);
1100                 }
1101                 free(ke);
1102                 break;
1103         }
1104         case Stat: {
1105                 struct stat st;
1106                 if (get_struct(pid, (void *)args[sc->offset], &st, sizeof(st))
1107                     != -1) {
1108                         char mode[12];
1109                         strmode(st.st_mode, mode);
1110                         asprintf(&tmp,
1111                             "{ mode=%s,inode=%jd,size=%jd,blksize=%ld }", mode,
1112                             (intmax_t)st.st_ino, (intmax_t)st.st_size,
1113                             (long)st.st_blksize);
1114                 } else {
1115                         asprintf(&tmp, "0x%lx", args[sc->offset]);
1116                 }
1117                 break;
1118         }
1119         case Rusage: {
1120                 struct rusage ru;
1121                 if (get_struct(pid, (void *)args[sc->offset], &ru, sizeof(ru))
1122                     != -1) {
1123                         asprintf(&tmp,
1124                             "{ u=%ld.%06ld,s=%ld.%06ld,in=%ld,out=%ld }",
1125                             (long)ru.ru_utime.tv_sec, ru.ru_utime.tv_usec,
1126                             (long)ru.ru_stime.tv_sec, ru.ru_stime.tv_usec,
1127                             ru.ru_inblock, ru.ru_oublock);
1128                 } else
1129                         asprintf(&tmp, "0x%lx", args[sc->offset]);
1130                 break;
1131         }
1132         case Rlimit: {
1133                 struct rlimit rl;
1134                 if (get_struct(pid, (void *)args[sc->offset], &rl, sizeof(rl))
1135                     != -1) {
1136                         asprintf(&tmp, "{ cur=%ju,max=%ju }",
1137                             rl.rlim_cur, rl.rlim_max);
1138                 } else
1139                         asprintf(&tmp, "0x%lx", args[sc->offset]);
1140                 break;
1141         }
1142         default:
1143                 errx(1, "Invalid argument type %d\n", sc->type & ARG_MASK);
1144         }
1145         return (tmp);
1146 }
1147
1148 /*
1149  * print_syscall
1150  * Print (to outfile) the system call and its arguments.  Note that
1151  * nargs is the number of arguments (not the number of words; this is
1152  * potentially confusing, I know).
1153  */
1154
1155 void
1156 print_syscall(struct trussinfo *trussinfo, const char *name, int nargs,
1157     char **s_args)
1158 {
1159         struct timespec timediff;
1160         int i, len;
1161
1162         len = 0;
1163         if (trussinfo->flags & FOLLOWFORKS)
1164                 len += fprintf(trussinfo->outfile, "%5d: ", trussinfo->pid);
1165
1166         if (name != NULL && (strcmp(name, "execve") == 0 ||
1167             strcmp(name, "exit") == 0)) {
1168                 clock_gettime(CLOCK_REALTIME, &trussinfo->curthread->after);
1169         }
1170
1171         if (trussinfo->flags & ABSOLUTETIMESTAMPS) {
1172                 timespecsubt(&trussinfo->curthread->after,
1173                     &trussinfo->start_time, &timediff);
1174                 len += fprintf(trussinfo->outfile, "%ld.%09ld ",
1175                     (long)timediff.tv_sec, timediff.tv_nsec);
1176         }
1177
1178         if (trussinfo->flags & RELATIVETIMESTAMPS) {
1179                 timespecsubt(&trussinfo->curthread->after,
1180                     &trussinfo->curthread->before, &timediff);
1181                 len += fprintf(trussinfo->outfile, "%ld.%09ld ",
1182                     (long)timediff.tv_sec, timediff.tv_nsec);
1183         }
1184
1185         len += fprintf(trussinfo->outfile, "%s(", name);
1186
1187         for (i = 0; i < nargs; i++) {
1188                 if (s_args[i])
1189                         len += fprintf(trussinfo->outfile, "%s", s_args[i]);
1190                 else
1191                         len += fprintf(trussinfo->outfile,
1192                             "<missing argument>");
1193                 len += fprintf(trussinfo->outfile, "%s", i < (nargs - 1) ?
1194                     "," : "");
1195         }
1196         len += fprintf(trussinfo->outfile, ")");
1197         for (i = 0; i < 6 - (len / 8); i++)
1198                 fprintf(trussinfo->outfile, "\t");
1199 }
1200
1201 void
1202 print_syscall_ret(struct trussinfo *trussinfo, const char *name, int nargs,
1203     char **s_args, int errorp, long retval, struct syscall *sc)
1204 {
1205         struct timespec timediff;
1206
1207         if (trussinfo->flags & COUNTONLY) {
1208                 if (!sc)
1209                         return;
1210                 clock_gettime(CLOCK_REALTIME, &trussinfo->curthread->after);
1211                 timespecsubt(&trussinfo->curthread->after,
1212                     &trussinfo->curthread->before, &timediff);
1213                 timespecadd(&sc->time, &timediff, &sc->time);
1214                 sc->ncalls++;
1215                 if (errorp)
1216                         sc->nerror++;
1217                 return;
1218         }
1219
1220         print_syscall(trussinfo, name, nargs, s_args);
1221         fflush(trussinfo->outfile);
1222         if (errorp)
1223                 fprintf(trussinfo->outfile, " ERR#%ld '%s'\n", retval,
1224                     strerror(retval));
1225         else {
1226                 /*
1227                  * Because pipe(2) has a special assembly glue to provide the
1228                  * libc API, we have to adjust retval.
1229                  */
1230                 if (name != NULL && strcmp(name, "pipe") == 0)
1231                         retval = 0;
1232                 fprintf(trussinfo->outfile, " = %ld (0x%lx)\n", retval, retval);
1233         }
1234 }
1235
1236 void
1237 print_summary(struct trussinfo *trussinfo)
1238 {
1239         struct timespec total = {0, 0};
1240         struct syscall *sc;
1241         int ncall, nerror;
1242
1243         fprintf(trussinfo->outfile, "%-20s%15s%8s%8s\n",
1244             "syscall", "seconds", "calls", "errors");
1245         ncall = nerror = 0;
1246         for (sc = syscalls; sc->name != NULL; sc++)
1247                 if (sc->ncalls) {
1248                         fprintf(trussinfo->outfile, "%-20s%5jd.%09ld%8d%8d\n",
1249                             sc->name, (intmax_t)sc->time.tv_sec,
1250                             sc->time.tv_nsec, sc->ncalls, sc->nerror);
1251                         timespecadd(&total, &sc->time, &total);
1252                         ncall += sc->ncalls;
1253                         nerror += sc->nerror;
1254                 }
1255         fprintf(trussinfo->outfile, "%20s%15s%8s%8s\n",
1256             "", "-------------", "-------", "-------");
1257         fprintf(trussinfo->outfile, "%-20s%5jd.%09ld%8d%8d\n",
1258             "", (intmax_t)total.tv_sec, total.tv_nsec, ncall, nerror);
1259 }