xref: /freebsd/usr.bin/truss/syscalls.c (revision 5ca34122ecdd5abc62bdae39663fec9ac8523d87)
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/types.h>
43 #include <sys/mman.h>
44 #include <sys/procctl.h>
45 #include <sys/ptrace.h>
46 #include <sys/socket.h>
47 #include <sys/time.h>
48 #include <sys/un.h>
49 #include <sys/wait.h>
50 #include <netinet/in.h>
51 #include <arpa/inet.h>
52 #include <sys/ioccom.h>
53 #include <machine/atomic.h>
54 #include <errno.h>
55 #include <sys/umtx.h>
56 #include <sys/event.h>
57 #include <sys/stat.h>
58 #include <sys/resource.h>
59 #include <machine/sysarch.h>
60 
61 #include <ctype.h>
62 #include <err.h>
63 #include <fcntl.h>
64 #include <poll.h>
65 #include <signal.h>
66 #include <stdint.h>
67 #include <stdio.h>
68 #include <stdlib.h>
69 #include <string.h>
70 #include <time.h>
71 #include <unistd.h>
72 #include <vis.h>
73 
74 #include "truss.h"
75 #include "extern.h"
76 #include "syscall.h"
77 
78 /* 64-bit alignment on 32-bit platforms. */
79 #ifdef __powerpc__
80 #define	QUAD_ALIGN	1
81 #else
82 #define	QUAD_ALIGN	0
83 #endif
84 
85 /* Number of slots needed for a 64-bit argument. */
86 #ifdef __LP64__
87 #define	QUAD_SLOTS	1
88 #else
89 #define	QUAD_SLOTS	2
90 #endif
91 
92 /*
93  * This should probably be in its own file, sorted alphabetically.
94  */
95 static struct syscall syscalls[] = {
96 	{ .name = "fcntl", .ret_type = 1, .nargs = 3,
97 	  .args = { { Int, 0 }, { Fcntl, 1 }, { Fcntlflag, 2 } } },
98 	{ .name = "fork", .ret_type = 1, .nargs = 0 },
99 	{ .name = "vfork", .ret_type = 1, .nargs = 0 },
100 	{ .name = "rfork", .ret_type = 1, .nargs = 1,
101 	  .args = { { Rforkflags, 0 } } },
102 	{ .name = "getegid", .ret_type = 1, .nargs = 0 },
103 	{ .name = "geteuid", .ret_type = 1, .nargs = 0 },
104 	{ .name = "linux_readlink", .ret_type = 1, .nargs = 3,
105 	  .args = { { Name, 0 }, { Name | OUT, 1 }, { Int, 2 } } },
106 	{ .name = "linux_socketcall", .ret_type = 1, .nargs = 2,
107 	  .args = { { Int, 0 }, { LinuxSockArgs, 1 } } },
108 	{ .name = "getgid", .ret_type = 1, .nargs = 0 },
109 	{ .name = "getpid", .ret_type = 1, .nargs = 0 },
110 	{ .name = "getpgid", .ret_type = 1, .nargs = 1,
111 	  .args = { { Int, 0 } } },
112 	{ .name = "getpgrp", .ret_type = 1, .nargs = 0 },
113 	{ .name = "getppid", .ret_type = 1, .nargs = 0 },
114 	{ .name = "getsid", .ret_type = 1, .nargs = 1,
115 	  .args = { { Int, 0 } } },
116 	{ .name = "getuid", .ret_type = 1, .nargs = 0 },
117 	{ .name = "issetugid", .ret_type = 1, .nargs = 0 },
118 	{ .name = "readlink", .ret_type = 1, .nargs = 3,
119 	  .args = { { Name, 0 }, { Readlinkres | OUT, 1 }, { Int, 2 } } },
120 	{ .name = "readlinkat", .ret_type = 1, .nargs = 4,
121 	  .args = { { Atfd, 0 }, { Name, 1 }, { Readlinkres | OUT, 2 },
122 		    { Int, 3 } } },
123 	{ .name = "lseek", .ret_type = 2, .nargs = 3,
124 	  .args = { { Int, 0 }, { Quad, 1 + QUAD_ALIGN },
125 		    { Whence, 1 + QUAD_SLOTS + QUAD_ALIGN } } },
126 	{ .name = "linux_lseek", .ret_type = 2, .nargs = 3,
127 	  .args = { { Int, 0 }, { Int, 1 }, { Whence, 2 } } },
128 	{ .name = "mmap", .ret_type = 2, .nargs = 6,
129 	  .args = { { Ptr, 0 }, { Int, 1 }, { Mprot, 2 }, { Mmapflags, 3 },
130 		    { Int, 4 }, { Quad, 5 + QUAD_ALIGN } } },
131 	{ .name = "linux_mkdir", .ret_type = 1, .nargs = 2,
132 	  .args = { { Name | IN, 0 }, { Int, 1 } } },
133 	{ .name = "mprotect", .ret_type = 1, .nargs = 3,
134 	  .args = { { Ptr, 0 }, { Int, 1 }, { Mprot, 2 } } },
135 	{ .name = "open", .ret_type = 1, .nargs = 3,
136 	  .args = { { Name | IN, 0 }, { Open, 1 }, { Octal, 2 } } },
137 	{ .name = "openat", .ret_type = 1, .nargs = 4,
138 	  .args = { { Atfd, 0 }, { Name | IN, 1 }, { Open, 2 },
139 		    { Octal, 3 } } },
140 	{ .name = "mkdir", .ret_type = 1, .nargs = 2,
141 	  .args = { { Name, 0 }, { Octal, 1 } } },
142 	{ .name = "mkdirat", .ret_type = 1, .nargs = 3,
143 	  .args = { { Atfd, 0 }, { Name, 1 }, { Octal, 2 } } },
144 	{ .name = "linux_open", .ret_type = 1, .nargs = 3,
145 	  .args = { { Name, 0 }, { Hex, 1 }, { Octal, 2 } } },
146 	{ .name = "close", .ret_type = 1, .nargs = 1,
147 	  .args = { { Int, 0 } } },
148 	{ .name = "link", .ret_type = 0, .nargs = 2,
149 	  .args = { { Name, 0 }, { Name, 1 } } },
150 	{ .name = "linkat", .ret_type = 0, .nargs = 5,
151 	  .args = { { Atfd, 0 }, { Name, 1 }, { Atfd, 2 }, { Name, 3 },
152 		    { Atflags, 4 } } },
153 	{ .name = "unlink", .ret_type = 0, .nargs = 1,
154 	  .args = { { Name, 0 } } },
155 	{ .name = "unlinkat", .ret_type = 0, .nargs = 3,
156 	  .args = { { Atfd, 0 }, { Name, 1 }, { Atflags, 2 } } },
157 	{ .name = "chdir", .ret_type = 0, .nargs = 1,
158 	  .args = { { Name, 0 } } },
159 	{ .name = "chroot", .ret_type = 0, .nargs = 1,
160 	  .args = { { Name, 0 } } },
161 	{ .name = "mkfifo", .ret_type = 0, .nargs = 2,
162 	  .args = { { Name, 0 }, { Octal, 1 } } },
163 	{ .name = "mkfifoat", .ret_type = 0, .nargs = 3,
164 	  .args = { { Atfd, 0 }, { Name, 1 }, { Octal, 2 } } },
165 	{ .name = "mknod", .ret_type = 0, .nargs = 3,
166 	  .args = { { Name, 0 }, { Octal, 1 }, { Int, 2 } } },
167 	{ .name = "mknodat", .ret_type = 0, .nargs = 4,
168 	  .args = { { Atfd, 0 }, { Name, 1 }, { Octal, 2 }, { Int, 3 } } },
169 	{ .name = "chmod", .ret_type = 0, .nargs = 2,
170 	  .args = { { Name, 0 }, { Octal, 1 } } },
171 	{ .name = "fchmod", .ret_type = 0, .nargs = 2,
172 	  .args = { { Int, 0 }, { Octal, 1 } } },
173 	{ .name = "lchmod", .ret_type = 0, .nargs = 2,
174 	  .args = { { Name, 0 }, { Octal, 1 } } },
175 	{ .name = "fchmodat", .ret_type = 0, .nargs = 4,
176 	  .args = { { Atfd, 0 }, { Name, 1 }, { Octal, 2 }, { Atflags, 3 } } },
177 	{ .name = "chown", .ret_type = 0, .nargs = 3,
178 	  .args = { { Name, 0 }, { Int, 1 }, { Int, 2 } } },
179 	{ .name = "fchown", .ret_type = 0, .nargs = 3,
180 	  .args = { { Int, 0 }, { Int, 1 }, { Int, 2 } } },
181 	{ .name = "lchown", .ret_type = 0, .nargs = 3,
182 	  .args = { { Name, 0 }, { Int, 1 }, { Int, 2 } } },
183 	{ .name = "fchownat", .ret_type = 0, .nargs = 5,
184 	  .args = { { Atfd, 0 }, { Name, 1 }, { Int, 2 }, { Int, 3 },
185 		    { Atflags, 4 } } },
186 	{ .name = "linux_stat64", .ret_type = 1, .nargs = 3,
187 	  .args = { { Name | IN, 0 }, { Ptr | OUT, 1 }, { Ptr | IN, 1 } } },
188 	{ .name = "mount", .ret_type = 0, .nargs = 4,
189 	  .args = { { Name, 0 }, { Name, 1 }, { Int, 2 }, { Ptr, 3 } } },
190 	{ .name = "umount", .ret_type = 0, .nargs = 2,
191 	  .args = { { Name, 0 }, { Int, 2 } } },
192 	{ .name = "fstat", .ret_type = 1, .nargs = 2,
193 	  .args = { { Int, 0 }, { Stat | OUT, 1 } } },
194 	{ .name = "fstatat", .ret_type = 1, .nargs = 4,
195 	  .args = { { Atfd, 0 }, { Name | IN, 1 }, { Stat | OUT, 2 },
196 		    { Atflags, 3 } } },
197 	{ .name = "stat", .ret_type = 1, .nargs = 2,
198 	  .args = { { Name | IN, 0 }, { Stat | OUT, 1 } } },
199 	{ .name = "lstat", .ret_type = 1, .nargs = 2,
200 	  .args = { { Name | IN, 0 }, { Stat | OUT, 1 } } },
201 	{ .name = "linux_newstat", .ret_type = 1, .nargs = 2,
202 	  .args = { { Name | IN, 0 }, { Ptr | OUT, 1 } } },
203 	{ .name = "linux_access", .ret_type = 1, .nargs = 2,
204 	  .args = { { Name, 0 }, { Accessmode, 1 } } },
205 	{ .name = "linux_newfstat", .ret_type = 1, .nargs = 2,
206 	  .args = { { Int, 0 }, { Ptr | OUT, 1 } } },
207 	{ .name = "write", .ret_type = 1, .nargs = 3,
208 	  .args = { { Int, 0 }, { BinString | IN, 1 }, { Int, 2 } } },
209 	{ .name = "ioctl", .ret_type = 1, .nargs = 3,
210 	  .args = { { Int, 0 }, { Ioctl, 1 }, { Hex, 2 } } },
211 	{ .name = "break", .ret_type = 1, .nargs = 1,
212 	  .args = { { Ptr, 0 } } },
213 	{ .name = "exit", .ret_type = 0, .nargs = 1,
214 	  .args = { { Hex, 0 } } },
215 	{ .name = "access", .ret_type = 1, .nargs = 2,
216 	  .args = { { Name | IN, 0 }, { Accessmode, 1 } } },
217 	{ .name = "eaccess", .ret_type = 1, .nargs = 2,
218 	  .args = { { Name | IN, 0 }, { Accessmode, 1 } } },
219 	{ .name = "faccessat", .ret_type = 1, .nargs = 4,
220 	  .args = { { Atfd, 0 }, { Name | IN, 1 }, { Accessmode, 2 },
221 		    { Atflags, 3 } } },
222 	{ .name = "sigaction", .ret_type = 1, .nargs = 3,
223 	  .args = { { Signal, 0 }, { Sigaction | IN, 1 },
224 		    { Sigaction | OUT, 2 } } },
225 	{ .name = "accept", .ret_type = 1, .nargs = 3,
226 	  .args = { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } },
227 	{ .name = "bind", .ret_type = 1, .nargs = 3,
228 	  .args = { { Int, 0 }, { Sockaddr | IN, 1 }, { Int, 2 } } },
229 	{ .name = "bindat", .ret_type = 1, .nargs = 4,
230 	  .args = { { Atfd, 0 }, { Int, 1 }, { Sockaddr | IN, 2 },
231 		    { Int, 3 } } },
232 	{ .name = "connect", .ret_type = 1, .nargs = 3,
233 	  .args = { { Int, 0 }, { Sockaddr | IN, 1 }, { Int, 2 } } },
234 	{ .name = "connectat", .ret_type = 1, .nargs = 4,
235 	  .args = { { Atfd, 0 }, { Int, 1 }, { Sockaddr | IN, 2 },
236 		    { Int, 3 } } },
237 	{ .name = "getpeername", .ret_type = 1, .nargs = 3,
238 	  .args = { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } },
239 	{ .name = "getsockname", .ret_type = 1, .nargs = 3,
240 	  .args = { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } },
241 	{ .name = "recvfrom", .ret_type = 1, .nargs = 6,
242 	  .args = { { Int, 0 }, { BinString | OUT, 1 }, { Int, 2 }, { Hex, 3 },
243 		    { Sockaddr | OUT, 4 }, { Ptr | OUT, 5 } } },
244 	{ .name = "sendto", .ret_type = 1, .nargs = 6,
245 	  .args = { { Int, 0 }, { BinString | IN, 1 }, { Int, 2 }, { Hex, 3 },
246 		    { Sockaddr | IN, 4 }, { Ptr | IN, 5 } } },
247 	{ .name = "execve", .ret_type = 1, .nargs = 3,
248 	  .args = { { Name | IN, 0 }, { ExecArgs | IN, 1 },
249 		    { ExecEnv | IN, 2 } } },
250 	{ .name = "linux_execve", .ret_type = 1, .nargs = 3,
251 	  .args = { { Name | IN, 0 }, { ExecArgs | IN, 1 },
252 		    { ExecEnv | IN, 2 } } },
253 	{ .name = "kldload", .ret_type = 0, .nargs = 1,
254 	  .args = { { Name | IN, 0 } } },
255 	{ .name = "kldunload", .ret_type = 0, .nargs = 1,
256 	  .args = { { Int, 0 } } },
257 	{ .name = "kldfind", .ret_type = 0, .nargs = 1,
258 	  .args = { { Name | IN, 0 } } },
259 	{ .name = "kldnext", .ret_type = 0, .nargs = 1,
260 	  .args = { { Int, 0 } } },
261 	{ .name = "kldstat", .ret_type = 0, .nargs = 2,
262 	  .args = { { Int, 0 }, { Ptr, 1 } } },
263 	{ .name = "kldfirstmod", .ret_type = 0, .nargs = 1,
264 	  .args = { { Int, 0 } } },
265 	{ .name = "nanosleep", .ret_type = 0, .nargs = 1,
266 	  .args = { { Timespec, 0 } } },
267 	{ .name = "select", .ret_type = 1, .nargs = 5,
268 	  .args = { { Int, 0 }, { Fd_set, 1 }, { Fd_set, 2 }, { Fd_set, 3 },
269 		    { Timeval, 4 } } },
270 	{ .name = "poll", .ret_type = 1, .nargs = 3,
271 	  .args = { { Pollfd, 0 }, { Int, 1 }, { Int, 2 } } },
272 	{ .name = "gettimeofday", .ret_type = 1, .nargs = 2,
273 	  .args = { { Timeval | OUT, 0 }, { Ptr, 1 } } },
274 	{ .name = "clock_gettime", .ret_type = 1, .nargs = 2,
275 	  .args = { { Int, 0 }, { Timespec | OUT, 1 } } },
276 	{ .name = "getitimer", .ret_type = 1, .nargs = 2,
277 	  .args = { { Int, 0 }, { Itimerval | OUT, 2 } } },
278 	{ .name = "setitimer", .ret_type = 1, .nargs = 3,
279 	  .args = { { Int, 0 }, { Itimerval, 1 }, { Itimerval | OUT, 2 } } },
280 	{ .name = "kse_release", .ret_type = 0, .nargs = 1,
281 	  .args = { { Timespec, 0 } } },
282 	{ .name = "kevent", .ret_type = 0, .nargs = 6,
283 	  .args = { { Int, 0 }, { Kevent, 1 }, { Int, 2 }, { Kevent | OUT, 3 },
284 		    { Int, 4 }, { Timespec, 5 } } },
285 	{ .name = "sigpending", .ret_type = 0, .nargs = 1,
286 	  .args = { { Sigset | OUT, 0 } } },
287 	{ .name = "sigprocmask", .ret_type = 0, .nargs = 3,
288 	  .args = { { Sigprocmask, 0 }, { Sigset, 1 }, { Sigset | OUT, 2 } } },
289 	{ .name = "sigqueue", .ret_type = 0, .nargs = 3,
290 	  .args = { { Int, 0 }, { Signal, 1 }, { LongHex, 2 } } },
291 	{ .name = "sigreturn", .ret_type = 0, .nargs = 1,
292 	  .args = { { Ptr, 0 } } },
293 	{ .name = "sigsuspend", .ret_type = 0, .nargs = 1,
294 	  .args = { { Sigset | IN, 0 } } },
295 	{ .name = "sigtimedwait", .ret_type = 1, .nargs = 3,
296 	  .args = { { Sigset | IN, 0 }, { Ptr, 1 }, { Timespec | IN, 2 } } },
297 	{ .name = "sigwait", .ret_type = 1, .nargs = 2,
298 	  .args = { { Sigset | IN, 0 }, { Ptr, 1 } } },
299 	{ .name = "sigwaitinfo", .ret_type = 1, .nargs = 2,
300 	  .args = { { Sigset | IN, 0 }, { Ptr, 1 } } },
301 	{ .name = "unmount", .ret_type = 1, .nargs = 2,
302 	  .args = { { Name, 0 }, { Int, 1 } } },
303 	{ .name = "socket", .ret_type = 1, .nargs = 3,
304 	  .args = { { Sockdomain, 0 }, { Socktype, 1 }, { Int, 2 } } },
305 	{ .name = "getrusage", .ret_type = 1, .nargs = 2,
306 	  .args = { { Int, 0 }, { Rusage | OUT, 1 } } },
307 	{ .name = "__getcwd", .ret_type = 1, .nargs = 2,
308 	  .args = { { Name | OUT, 0 }, { Int, 1 } } },
309 	{ .name = "shutdown", .ret_type = 1, .nargs = 2,
310 	  .args = { { Int, 0 }, { Shutdown, 1 } } },
311 	{ .name = "getrlimit", .ret_type = 1, .nargs = 2,
312 	  .args = { { Resource, 0 }, { Rlimit | OUT, 1 } } },
313 	{ .name = "setrlimit", .ret_type = 1, .nargs = 2,
314 	  .args = { { Resource, 0 }, { Rlimit | IN, 1 } } },
315 	{ .name = "utimes", .ret_type = 1, .nargs = 2,
316 	  .args = { { Name | IN, 0 }, { Timeval2 | IN, 1 } } },
317 	{ .name = "lutimes", .ret_type = 1, .nargs = 2,
318 	  .args = { { Name | IN, 0 }, { Timeval2 | IN, 1 } } },
319 	{ .name = "futimes", .ret_type = 1, .nargs = 2,
320 	  .args = { { Int, 0 }, { Timeval2 | IN, 1 } } },
321 	{ .name = "futimesat", .ret_type = 1, .nargs = 3,
322 	  .args = { { Atfd, 0 }, { Name | IN, 1 }, { Timeval2 | IN, 2 } } },
323 	{ .name = "futimens", .ret_type = 1, .nargs = 2,
324 	  .args = { { Int, 0 }, { Timespec2 | IN, 1 } } },
325 	{ .name = "utimensat", .ret_type = 1, .nargs = 4,
326 	  .args = { { Atfd, 0 }, { Name | IN, 1 }, { Timespec2 | IN, 2 },
327 		    { Atflags, 3 } } },
328 	{ .name = "chflags", .ret_type = 1, .nargs = 2,
329 	  .args = { { Name | IN, 0 }, { Hex, 1 } } },
330 	{ .name = "lchflags", .ret_type = 1, .nargs = 2,
331 	  .args = { { Name | IN, 0 }, { Hex, 1 } } },
332 	{ .name = "pathconf", .ret_type = 1, .nargs = 2,
333 	  .args = { { Name | IN, 0 }, { Pathconf, 1 } } },
334 	{ .name = "pipe", .ret_type = 1, .nargs = 1,
335 	  .args = { { Ptr, 0 } } },
336 	{ .name = "pipe2", .ret_type = 1, .nargs = 2,
337 	  .args = { { Ptr, 0 }, { Open, 1 } } },
338 	{ .name = "truncate", .ret_type = 1, .nargs = 3,
339 	  .args = { { Name | IN, 0 }, { Int | IN, 1 }, { Quad | IN, 2 } } },
340 	{ .name = "ftruncate", .ret_type = 1, .nargs = 3,
341 	  .args = { { Int | IN, 0 }, { Int | IN, 1 }, { Quad | IN, 2 } } },
342 	{ .name = "kill", .ret_type = 1, .nargs = 2,
343 	  .args = { { Int | IN, 0 }, { Signal | IN, 1 } } },
344 	{ .name = "munmap", .ret_type = 1, .nargs = 2,
345 	  .args = { { Ptr, 0 }, { Int, 1 } } },
346 	{ .name = "read", .ret_type = 1, .nargs = 3,
347 	  .args = { { Int, 0 }, { BinString | OUT, 1 }, { Int, 2 } } },
348 	{ .name = "rename", .ret_type = 1, .nargs = 2,
349 	  .args = { { Name, 0 }, { Name, 1 } } },
350 	{ .name = "renameat", .ret_type = 1, .nargs = 4,
351 	  .args = { { Atfd, 0 }, { Name, 1 }, { Atfd, 2 }, { Name, 3 } } },
352 	{ .name = "symlink", .ret_type = 1, .nargs = 2,
353 	  .args = { { Name, 0 }, { Name, 1 } } },
354 	{ .name = "symlinkat", .ret_type = 1, .nargs = 3,
355 	  .args = { { Name, 0 }, { Atfd, 1 }, { Name, 2 } } },
356 	{ .name = "posix_openpt", .ret_type = 1, .nargs = 1,
357 	  .args = { { Open, 0 } } },
358 	{ .name = "wait4", .ret_type = 1, .nargs = 4,
359 	  .args = { { Int, 0 }, { ExitStatus | OUT, 1 }, { Waitoptions, 2 },
360 		    { Rusage | OUT, 3 } } },
361 	{ .name = "wait6", .ret_type = 1, .nargs = 6,
362 	  .args = { { Idtype, 0 }, { Int, 1 }, { ExitStatus | OUT, 2 },
363 		    { Waitoptions, 3 }, { Rusage | OUT, 4 }, { Ptr, 5 } } },
364 	{ .name = "procctl", .ret_type = 1, .nargs = 4,
365 	  .args = { { Idtype, 0 }, { Int, 1 }, { Procctl, 2 }, { Ptr, 3 } } },
366 	{ .name = "sysarch", .ret_type = 1, .nargs = 2,
367 	  .args = { { Sysarch, 0 }, { Ptr, 1 } } },
368 	{ .name = "_umtx_op", .ret_type = 1, .nargs = 5,
369 	  .args = { { Ptr, 0 }, { Umtxop, 1 }, { LongHex, 2 }, { Ptr, 3 },
370 		    { Ptr, 4 } } },
371 	{ .name = "thr_kill", .ret_type = 0, .nargs = 2,
372 	  .args = { { Long, 0 }, { Signal, 1 } } },
373 	{ .name = "thr_self", .ret_type = 0, .nargs = 1,
374 	  .args = { { Ptr, 0 } } },
375 	{ .name = 0 },
376 };
377 
378 /* Xlat idea taken from strace */
379 struct xlat {
380 	int val;
381 	const char *str;
382 };
383 
384 #define	X(a)	{ a, #a },
385 #define	XEND	{ 0, NULL }
386 
387 static struct xlat kevent_filters[] = {
388 	X(EVFILT_READ) X(EVFILT_WRITE) X(EVFILT_AIO) X(EVFILT_VNODE)
389 	X(EVFILT_PROC) X(EVFILT_SIGNAL) X(EVFILT_TIMER)
390 	X(EVFILT_PROCDESC) X(EVFILT_FS) X(EVFILT_LIO) X(EVFILT_USER)
391 	X(EVFILT_SENDFILE) XEND
392 };
393 
394 static struct xlat kevent_flags[] = {
395 	X(EV_ADD) X(EV_DELETE) X(EV_ENABLE) X(EV_DISABLE) X(EV_ONESHOT)
396 	X(EV_CLEAR) X(EV_RECEIPT) X(EV_DISPATCH) X(EV_FORCEONESHOT)
397 	X(EV_DROP) X(EV_FLAG1) X(EV_ERROR) X(EV_EOF) XEND
398 };
399 
400 static struct xlat kevent_user_ffctrl[] = {
401 	X(NOTE_FFNOP) X(NOTE_FFAND) X(NOTE_FFOR) X(NOTE_FFCOPY)
402 	XEND
403 };
404 
405 static struct xlat kevent_rdwr_fflags[] = {
406 	X(NOTE_LOWAT) X(NOTE_FILE_POLL) XEND
407 };
408 
409 static struct xlat kevent_vnode_fflags[] = {
410 	X(NOTE_DELETE) X(NOTE_WRITE) X(NOTE_EXTEND) X(NOTE_ATTRIB)
411 	X(NOTE_LINK) X(NOTE_RENAME) X(NOTE_REVOKE) XEND
412 };
413 
414 static struct xlat kevent_proc_fflags[] = {
415 	X(NOTE_EXIT) X(NOTE_FORK) X(NOTE_EXEC) X(NOTE_TRACK) X(NOTE_TRACKERR)
416 	X(NOTE_CHILD) XEND
417 };
418 
419 static struct xlat kevent_timer_fflags[] = {
420 	X(NOTE_SECONDS) X(NOTE_MSECONDS) X(NOTE_USECONDS) X(NOTE_NSECONDS)
421 	XEND
422 };
423 
424 static struct xlat poll_flags[] = {
425 	X(POLLSTANDARD) X(POLLIN) X(POLLPRI) X(POLLOUT) X(POLLERR)
426 	X(POLLHUP) X(POLLNVAL) X(POLLRDNORM) X(POLLRDBAND)
427 	X(POLLWRBAND) X(POLLINIGNEOF) XEND
428 };
429 
430 static struct xlat mmap_flags[] = {
431 	X(MAP_SHARED) X(MAP_PRIVATE) X(MAP_FIXED) X(MAP_RESERVED0020)
432 	X(MAP_RESERVED0040) X(MAP_RESERVED0080) X(MAP_RESERVED0100)
433 	X(MAP_HASSEMAPHORE) X(MAP_STACK) X(MAP_NOSYNC) X(MAP_ANON)
434 	X(MAP_EXCL) X(MAP_NOCORE) X(MAP_PREFAULT_READ)
435 #ifdef MAP_32BIT
436 	X(MAP_32BIT)
437 #endif
438 	XEND
439 };
440 
441 static struct xlat mprot_flags[] = {
442 	X(PROT_NONE) X(PROT_READ) X(PROT_WRITE) X(PROT_EXEC) XEND
443 };
444 
445 static struct xlat whence_arg[] = {
446 	X(SEEK_SET) X(SEEK_CUR) X(SEEK_END) X(SEEK_DATA) X(SEEK_HOLE) XEND
447 };
448 
449 static struct xlat sigaction_flags[] = {
450 	X(SA_ONSTACK) X(SA_RESTART) X(SA_RESETHAND) X(SA_NOCLDSTOP)
451 	X(SA_NODEFER) X(SA_NOCLDWAIT) X(SA_SIGINFO) XEND
452 };
453 
454 static struct xlat fcntl_arg[] = {
455 	X(F_DUPFD) X(F_GETFD) X(F_SETFD) X(F_GETFL) X(F_SETFL)
456 	X(F_GETOWN) X(F_SETOWN) X(F_OGETLK) X(F_OSETLK) X(F_OSETLKW)
457 	X(F_DUP2FD) X(F_GETLK) X(F_SETLK) X(F_SETLKW) X(F_SETLK_REMOTE)
458 	X(F_READAHEAD) X(F_RDAHEAD) X(F_DUPFD_CLOEXEC) X(F_DUP2FD_CLOEXEC)
459 	XEND
460 };
461 
462 static struct xlat fcntlfd_arg[] = {
463 	X(FD_CLOEXEC) XEND
464 };
465 
466 static struct xlat fcntlfl_arg[] = {
467 	X(O_APPEND) X(O_ASYNC) X(O_FSYNC) X(O_NONBLOCK) X(O_NOFOLLOW)
468 	X(FRDAHEAD) X(O_DIRECT) XEND
469 };
470 
471 static struct xlat sockdomain_arg[] = {
472 	X(PF_UNSPEC) X(PF_LOCAL) X(PF_UNIX) X(PF_INET) X(PF_IMPLINK)
473 	X(PF_PUP) X(PF_CHAOS) X(PF_NETBIOS) X(PF_ISO) X(PF_OSI)
474 	X(PF_ECMA) X(PF_DATAKIT) X(PF_CCITT) X(PF_SNA) X(PF_DECnet)
475 	X(PF_DLI) X(PF_LAT) X(PF_HYLINK) X(PF_APPLETALK) X(PF_ROUTE)
476 	X(PF_LINK) X(PF_XTP) X(PF_COIP) X(PF_CNT) X(PF_SIP) X(PF_IPX)
477 	X(PF_RTIP) X(PF_PIP) X(PF_ISDN) X(PF_KEY) X(PF_INET6)
478 	X(PF_NATM) X(PF_ATM) X(PF_NETGRAPH) X(PF_SLOW) X(PF_SCLUSTER)
479 	X(PF_ARP) X(PF_BLUETOOTH) X(PF_IEEE80211) X(PF_INET_SDP)
480 	X(PF_INET6_SDP) XEND
481 };
482 
483 static struct xlat socktype_arg[] = {
484 	X(SOCK_STREAM) X(SOCK_DGRAM) X(SOCK_RAW) X(SOCK_RDM)
485 	X(SOCK_SEQPACKET) XEND
486 };
487 
488 static struct xlat open_flags[] = {
489 	X(O_RDONLY) X(O_WRONLY) X(O_RDWR) X(O_ACCMODE) X(O_NONBLOCK)
490 	X(O_APPEND) X(O_SHLOCK) X(O_EXLOCK) X(O_ASYNC) X(O_FSYNC)
491 	X(O_NOFOLLOW) X(O_CREAT) X(O_TRUNC) X(O_EXCL) X(O_NOCTTY)
492 	X(O_DIRECT) X(O_DIRECTORY) X(O_EXEC) X(O_TTY_INIT) X(O_CLOEXEC)
493 	X(O_VERIFY) XEND
494 };
495 
496 static struct xlat shutdown_arg[] = {
497 	X(SHUT_RD) X(SHUT_WR) X(SHUT_RDWR) XEND
498 };
499 
500 static struct xlat resource_arg[] = {
501 	X(RLIMIT_CPU) X(RLIMIT_FSIZE) X(RLIMIT_DATA) X(RLIMIT_STACK)
502 	X(RLIMIT_CORE) X(RLIMIT_RSS) X(RLIMIT_MEMLOCK) X(RLIMIT_NPROC)
503 	X(RLIMIT_NOFILE) X(RLIMIT_SBSIZE) X(RLIMIT_VMEM) X(RLIMIT_NPTS)
504 	X(RLIMIT_SWAP) X(RLIMIT_KQUEUES) XEND
505 };
506 
507 static struct xlat pathconf_arg[] = {
508 	X(_PC_LINK_MAX)  X(_PC_MAX_CANON)  X(_PC_MAX_INPUT)
509 	X(_PC_NAME_MAX) X(_PC_PATH_MAX) X(_PC_PIPE_BUF)
510 	X(_PC_CHOWN_RESTRICTED) X(_PC_NO_TRUNC) X(_PC_VDISABLE)
511 	X(_PC_ASYNC_IO) X(_PC_PRIO_IO) X(_PC_SYNC_IO)
512 	X(_PC_ALLOC_SIZE_MIN) X(_PC_FILESIZEBITS)
513 	X(_PC_REC_INCR_XFER_SIZE) X(_PC_REC_MAX_XFER_SIZE)
514 	X(_PC_REC_MIN_XFER_SIZE) X(_PC_REC_XFER_ALIGN)
515 	X(_PC_SYMLINK_MAX) X(_PC_ACL_EXTENDED) X(_PC_ACL_PATH_MAX)
516 	X(_PC_CAP_PRESENT) X(_PC_INF_PRESENT) X(_PC_MAC_PRESENT)
517 	X(_PC_ACL_NFS4) X(_PC_MIN_HOLE_SIZE) XEND
518 };
519 
520 static struct xlat rfork_flags[] = {
521 	X(RFFDG) X(RFPROC) X(RFMEM) X(RFNOWAIT) X(RFCFDG) X(RFTHREAD)
522 	X(RFSIGSHARE) X(RFLINUXTHPN) X(RFTSIGZMB) X(RFPPWAIT) XEND
523 };
524 
525 static struct xlat wait_options[] = {
526 	X(WNOHANG) X(WUNTRACED) X(WCONTINUED) X(WNOWAIT) X(WEXITED)
527 	X(WTRAPPED) XEND
528 };
529 
530 static struct xlat idtype_arg[] = {
531 	X(P_PID) X(P_PPID) X(P_PGID) X(P_SID) X(P_CID) X(P_UID) X(P_GID)
532 	X(P_ALL) X(P_LWPID) X(P_TASKID) X(P_PROJID) X(P_POOLID) X(P_JAILID)
533 	X(P_CTID) X(P_CPUID) X(P_PSETID) XEND
534 };
535 
536 static struct xlat procctl_arg[] = {
537 	X(PROC_SPROTECT) XEND
538 };
539 
540 static struct xlat umtx_ops[] = {
541 	X(UMTX_OP_RESERVED0) X(UMTX_OP_RESERVED1) X(UMTX_OP_WAIT)
542 	X(UMTX_OP_WAKE) X(UMTX_OP_MUTEX_TRYLOCK) X(UMTX_OP_MUTEX_LOCK)
543 	X(UMTX_OP_MUTEX_UNLOCK) X(UMTX_OP_SET_CEILING) X(UMTX_OP_CV_WAIT)
544 	X(UMTX_OP_CV_SIGNAL) X(UMTX_OP_CV_BROADCAST) X(UMTX_OP_WAIT_UINT)
545 	X(UMTX_OP_RW_RDLOCK) X(UMTX_OP_RW_WRLOCK) X(UMTX_OP_RW_UNLOCK)
546 	X(UMTX_OP_WAIT_UINT_PRIVATE) X(UMTX_OP_WAKE_PRIVATE)
547 	X(UMTX_OP_MUTEX_WAIT) X(UMTX_OP_MUTEX_WAKE) X(UMTX_OP_SEM_WAIT)
548 	X(UMTX_OP_SEM_WAKE) X(UMTX_OP_NWAKE_PRIVATE) X(UMTX_OP_MUTEX_WAKE2)
549 	X(UMTX_OP_SEM2_WAIT) X(UMTX_OP_SEM2_WAKE)
550 	XEND
551 };
552 
553 static struct xlat at_flags[] = {
554 	X(AT_EACCESS) X(AT_SYMLINK_NOFOLLOW) X(AT_SYMLINK_FOLLOW)
555 	X(AT_REMOVEDIR) XEND
556 };
557 
558 static struct xlat access_modes[] = {
559 	X(R_OK) X(W_OK) X(X_OK) XEND
560 };
561 
562 static struct xlat sysarch_ops[] = {
563 #if defined(__i386__) || defined(__amd64__)
564 	X(I386_GET_LDT) X(I386_SET_LDT) X(I386_GET_IOPERM) X(I386_SET_IOPERM)
565 	X(I386_VM86) X(I386_GET_FSBASE) X(I386_SET_FSBASE) X(I386_GET_GSBASE)
566 	X(I386_SET_GSBASE) X(I386_GET_XFPUSTATE) X(AMD64_GET_FSBASE)
567 	X(AMD64_SET_FSBASE) X(AMD64_GET_GSBASE) X(AMD64_SET_GSBASE)
568 	X(AMD64_GET_XFPUSTATE)
569 #endif
570 	XEND
571 };
572 
573 static struct xlat linux_socketcall_ops[] = {
574 	X(LINUX_SOCKET) X(LINUX_BIND) X(LINUX_CONNECT) X(LINUX_LISTEN)
575 	X(LINUX_ACCEPT) X(LINUX_GETSOCKNAME) X(LINUX_GETPEERNAME)
576 	X(LINUX_SOCKETPAIR) X(LINUX_SEND) X(LINUX_RECV) X(LINUX_SENDTO)
577 	X(LINUX_RECVFROM) X(LINUX_SHUTDOWN) X(LINUX_SETSOCKOPT)
578 	X(LINUX_GETSOCKOPT) X(LINUX_SENDMSG) X(LINUX_RECVMSG)
579 	XEND
580 };
581 
582 static struct xlat sigprocmask_ops[] = {
583 	X(SIG_BLOCK) X(SIG_UNBLOCK) X(SIG_SETMASK)
584 	XEND
585 };
586 
587 #undef X
588 #undef XEND
589 
590 /*
591  * Searches an xlat array for a value, and returns it if found.  Otherwise
592  * return a string representation.
593  */
594 static const char *
595 lookup(struct xlat *xlat, int val, int base)
596 {
597 	static char tmp[16];
598 
599 	for (; xlat->str != NULL; xlat++)
600 		if (xlat->val == val)
601 			return (xlat->str);
602 	switch (base) {
603 		case 8:
604 			sprintf(tmp, "0%o", val);
605 			break;
606 		case 16:
607 			sprintf(tmp, "0x%x", val);
608 			break;
609 		case 10:
610 			sprintf(tmp, "%u", val);
611 			break;
612 		default:
613 			errx(1,"Unknown lookup base");
614 			break;
615 	}
616 	return (tmp);
617 }
618 
619 static const char *
620 xlookup(struct xlat *xlat, int val)
621 {
622 
623 	return (lookup(xlat, val, 16));
624 }
625 
626 /*
627  * Searches an xlat array containing bitfield values.  Remaining bits
628  * set after removing the known ones are printed at the end:
629  * IN|0x400.
630  */
631 static char *
632 xlookup_bits(struct xlat *xlat, int val)
633 {
634 	int len, rem;
635 	static char str[512];
636 
637 	len = 0;
638 	rem = val;
639 	for (; xlat->str != NULL; xlat++) {
640 		if ((xlat->val & rem) == xlat->val) {
641 			/*
642 			 * Don't print the "all-bits-zero" string unless all
643 			 * bits are really zero.
644 			 */
645 			if (xlat->val == 0 && val != 0)
646 				continue;
647 			len += sprintf(str + len, "%s|", xlat->str);
648 			rem &= ~(xlat->val);
649 		}
650 	}
651 
652 	/*
653 	 * If we have leftover bits or didn't match anything, print
654 	 * the remainder.
655 	 */
656 	if (rem || len == 0)
657 		len += sprintf(str + len, "0x%x", rem);
658 	if (len && str[len - 1] == '|')
659 		len--;
660 	str[len] = 0;
661 	return (str);
662 }
663 
664 /*
665  * If/when the list gets big, it might be desirable to do it
666  * as a hash table or binary search.
667  */
668 struct syscall *
669 get_syscall(const char *name)
670 {
671 	struct syscall *sc;
672 
673 	sc = syscalls;
674 	if (name == NULL)
675 		return (NULL);
676 	while (sc->name) {
677 		if (strcmp(name, sc->name) == 0)
678 			return (sc);
679 		sc++;
680 	}
681 	return (NULL);
682 }
683 
684 /*
685  * Copy a fixed amount of bytes from the process.
686  */
687 static int
688 get_struct(pid_t pid, void *offset, void *buf, int len)
689 {
690 	struct ptrace_io_desc iorequest;
691 
692 	iorequest.piod_op = PIOD_READ_D;
693 	iorequest.piod_offs = offset;
694 	iorequest.piod_addr = buf;
695 	iorequest.piod_len = len;
696 	if (ptrace(PT_IO, pid, (caddr_t)&iorequest, 0) < 0)
697 		return (-1);
698 	return (0);
699 }
700 
701 #define	MAXSIZE		4096
702 
703 /*
704  * Copy a string from the process.  Note that it is
705  * expected to be a C string, but if max is set, it will
706  * only get that much.
707  */
708 static char *
709 get_string(pid_t pid, void *addr, int max)
710 {
711 	struct ptrace_io_desc iorequest;
712 	char *buf, *nbuf;
713 	size_t offset, size, totalsize;
714 
715 	offset = 0;
716 	if (max)
717 		size = max + 1;
718 	else {
719 		/* Read up to the end of the current page. */
720 		size = PAGE_SIZE - ((uintptr_t)addr % PAGE_SIZE);
721 		if (size > MAXSIZE)
722 			size = MAXSIZE;
723 	}
724 	totalsize = size;
725 	buf = malloc(totalsize);
726 	if (buf == NULL)
727 		return (NULL);
728 	for (;;) {
729 		iorequest.piod_op = PIOD_READ_D;
730 		iorequest.piod_offs = (char *)addr + offset;
731 		iorequest.piod_addr = buf + offset;
732 		iorequest.piod_len = size;
733 		if (ptrace(PT_IO, pid, (caddr_t)&iorequest, 0) < 0) {
734 			free(buf);
735 			return (NULL);
736 		}
737 		if (memchr(buf + offset, '\0', size) != NULL)
738 			return (buf);
739 		offset += size;
740 		if (totalsize < MAXSIZE && max == 0) {
741 			size = MAXSIZE - totalsize;
742 			if (size > PAGE_SIZE)
743 				size = PAGE_SIZE;
744 			nbuf = realloc(buf, totalsize + size);
745 			if (nbuf == NULL) {
746 				buf[totalsize - 1] = '\0';
747 				return (buf);
748 			}
749 			buf = nbuf;
750 			totalsize += size;
751 		} else {
752 			buf[totalsize - 1] = '\0';
753 			return (buf);
754 		}
755 	}
756 }
757 
758 static char *
759 strsig2(int sig)
760 {
761 	static char tmp[sizeof(int) * 3 + 1];
762 	char *ret;
763 
764 	ret = strsig(sig);
765 	if (ret == NULL) {
766 		snprintf(tmp, sizeof(tmp), "%d", sig);
767 		ret = tmp;
768 	}
769 	return (ret);
770 }
771 
772 static void
773 print_kevent(FILE *fp, struct kevent *ke, int input)
774 {
775 
776 	switch (ke->filter) {
777 	case EVFILT_READ:
778 	case EVFILT_WRITE:
779 	case EVFILT_VNODE:
780 	case EVFILT_PROC:
781 	case EVFILT_TIMER:
782 	case EVFILT_PROCDESC:
783 		fprintf(fp, "%ju", (uintmax_t)ke->ident);
784 		break;
785 	case EVFILT_SIGNAL:
786 		fputs(strsig2(ke->ident), fp);
787 		break;
788 	default:
789 		fprintf(fp, "%p", (void *)ke->ident);
790 	}
791 	fprintf(fp, ",%s,%s,", xlookup(kevent_filters, ke->filter),
792 	    xlookup_bits(kevent_flags, ke->flags));
793 	switch (ke->filter) {
794 	case EVFILT_READ:
795 	case EVFILT_WRITE:
796 		fputs(xlookup_bits(kevent_rdwr_fflags, ke->fflags), fp);
797 		break;
798 	case EVFILT_VNODE:
799 		fputs(xlookup_bits(kevent_vnode_fflags, ke->fflags), fp);
800 		break;
801 	case EVFILT_PROC:
802 	case EVFILT_PROCDESC:
803 		fputs(xlookup_bits(kevent_proc_fflags, ke->fflags), fp);
804 		break;
805 	case EVFILT_TIMER:
806 		fputs(xlookup_bits(kevent_timer_fflags, ke->fflags), fp);
807 		break;
808 	case EVFILT_USER: {
809 		int ctrl, data;
810 
811 		ctrl = ke->fflags & NOTE_FFCTRLMASK;
812 		data = ke->fflags & NOTE_FFLAGSMASK;
813 		if (input) {
814 			fputs(xlookup(kevent_user_ffctrl, ctrl), fp);
815 			if (ke->fflags & NOTE_TRIGGER)
816 				fputs("|NOTE_TRIGGER", fp);
817 			if (data != 0)
818 				fprintf(fp, "|%#x", data);
819 		} else {
820 			fprintf(fp, "%#x", data);
821 		}
822 		break;
823 	}
824 	default:
825 		fprintf(fp, "%#x", ke->fflags);
826 	}
827 	fprintf(fp, ",%p,%p", (void *)ke->data, (void *)ke->udata);
828 }
829 
830 /*
831  * Converts a syscall argument into a string.  Said string is
832  * allocated via malloc(), so needs to be free()'d.  sc is
833  * a pointer to the syscall description (see above); args is
834  * an array of all of the system call arguments.
835  */
836 char *
837 print_arg(struct syscall_args *sc, unsigned long *args, long retval,
838     struct trussinfo *trussinfo)
839 {
840 	FILE *fp;
841 	char *tmp;
842 	size_t tmplen;
843 	pid_t pid;
844 
845 	fp = open_memstream(&tmp, &tmplen);
846 	pid = trussinfo->pid;
847 	switch (sc->type & ARG_MASK) {
848 	case Hex:
849 		fprintf(fp, "0x%x", (int)args[sc->offset]);
850 		break;
851 	case Octal:
852 		fprintf(fp, "0%o", (int)args[sc->offset]);
853 		break;
854 	case Int:
855 		fprintf(fp, "%d", (int)args[sc->offset]);
856 		break;
857 	case LongHex:
858 		fprintf(fp, "0x%lx", args[sc->offset]);
859 		break;
860 	case Long:
861 		fprintf(fp, "%ld", args[sc->offset]);
862 		break;
863 	case Name: {
864 		/* NULL-terminated string. */
865 		char *tmp2;
866 
867 		tmp2 = get_string(pid, (void*)args[sc->offset], 0);
868 		fprintf(fp, "\"%s\"", tmp2);
869 		free(tmp2);
870 		break;
871 	}
872 	case BinString: {
873 		/*
874 		 * Binary block of data that might have printable characters.
875 		 * XXX If type|OUT, assume that the length is the syscall's
876 		 * return value.  Otherwise, assume that the length of the block
877 		 * is in the next syscall argument.
878 		 */
879 		int max_string = trussinfo->strsize;
880 		char tmp2[max_string + 1], *tmp3;
881 		int len;
882 		int truncated = 0;
883 
884 		if (sc->type & OUT)
885 			len = retval;
886 		else
887 			len = args[sc->offset + 1];
888 
889 		/*
890 		 * Don't print more than max_string characters, to avoid word
891 		 * wrap.  If we have to truncate put some ... after the string.
892 		 */
893 		if (len > max_string) {
894 			len = max_string;
895 			truncated = 1;
896 		}
897 		if (len && get_struct(pid, (void*)args[sc->offset], &tmp2, len)
898 		    != -1) {
899 			tmp3 = malloc(len * 4 + 1);
900 			while (len) {
901 				if (strvisx(tmp3, tmp2, len,
902 				    VIS_CSTYLE|VIS_TAB|VIS_NL) <= max_string)
903 					break;
904 				len--;
905 				truncated = 1;
906 			};
907 			fprintf(fp, "\"%s\"%s", tmp3, truncated ?
908 			    "..." : "");
909 			free(tmp3);
910 		} else {
911 			fprintf(fp, "0x%lx", args[sc->offset]);
912 		}
913 		break;
914 	}
915 	case ExecArgs:
916 	case ExecEnv:
917 	case StringArray: {
918 		uintptr_t addr;
919 		union {
920 			char *strarray[0];
921 			char buf[PAGE_SIZE];
922 		} u;
923 		char *string;
924 		size_t len;
925 		int first, i;
926 
927 		/*
928 		 * Only parse argv[] and environment arrays from exec calls
929 		 * if requested.
930 		 */
931 		if (((sc->type & ARG_MASK) == ExecArgs &&
932 		    (trussinfo->flags & EXECVEARGS) == 0) ||
933 		    ((sc->type & ARG_MASK) == ExecEnv &&
934 		    (trussinfo->flags & EXECVEENVS) == 0)) {
935 			fprintf(fp, "0x%lx", args[sc->offset]);
936 			break;
937 		}
938 
939 		/*
940 		 * Read a page of pointers at a time.  Punt if the top-level
941 		 * pointer is not aligned.  Note that the first read is of
942 		 * a partial page.
943 		 */
944 		addr = args[sc->offset];
945 		if (addr % sizeof(char *) != 0) {
946 			fprintf(fp, "0x%lx", args[sc->offset]);
947 			break;
948 		}
949 
950 		len = PAGE_SIZE - (addr & PAGE_MASK);
951 		if (get_struct(pid, (void *)addr, u.buf, len) == -1) {
952 			fprintf(fp, "0x%lx", args[sc->offset]);
953 			break;
954 		}
955 
956 		fputc('[', fp);
957 		first = 1;
958 		i = 0;
959 		while (u.strarray[i] != NULL) {
960 			string = get_string(pid, u.strarray[i], 0);
961 			fprintf(fp, "%s \"%s\"", first ? "" : ",", string);
962 			free(string);
963 			first = 0;
964 
965 			i++;
966 			if (i == len / sizeof(char *)) {
967 				addr += len;
968 				len = PAGE_SIZE;
969 				if (get_struct(pid, (void *)addr, u.buf, len) ==
970 				    -1) {
971 					fprintf(fp, ", <inval>");
972 					break;
973 				}
974 				i = 0;
975 			}
976 		}
977 		fputs(" ]", fp);
978 		break;
979 	}
980 #ifdef __LP64__
981 	case Quad:
982 		fprintf(fp, "0x%lx", args[sc->offset]);
983 		break;
984 #else
985 	case Quad: {
986 		unsigned long long ll;
987 
988 		ll = *(unsigned long long *)(args + sc->offset);
989 		fprintf(fp, "0x%llx", ll);
990 		break;
991 	}
992 #endif
993 	case Ptr:
994 		fprintf(fp, "0x%lx", args[sc->offset]);
995 		break;
996 	case Readlinkres: {
997 		char *tmp2;
998 
999 		if (retval == -1)
1000 			break;
1001 		tmp2 = get_string(pid, (void*)args[sc->offset], retval);
1002 		fprintf(fp, "\"%s\"", tmp2);
1003 		free(tmp2);
1004 		break;
1005 	}
1006 	case Ioctl: {
1007 		const char *temp;
1008 		unsigned long cmd;
1009 
1010 		cmd = args[sc->offset];
1011 		temp = ioctlname(cmd);
1012 		if (temp)
1013 			fputs(temp, fp);
1014 		else {
1015 			fprintf(fp, "0x%lx { IO%s%s 0x%lx('%c'), %lu, %lu }",
1016 			    cmd, cmd & IOC_OUT ? "R" : "",
1017 			    cmd & IOC_IN ? "W" : "", IOCGROUP(cmd),
1018 			    isprint(IOCGROUP(cmd)) ? (char)IOCGROUP(cmd) : '?',
1019 			    cmd & 0xFF, IOCPARM_LEN(cmd));
1020 		}
1021 		break;
1022 	}
1023 	case Timespec: {
1024 		struct timespec ts;
1025 
1026 		if (get_struct(pid, (void *)args[sc->offset], &ts,
1027 		    sizeof(ts)) != -1)
1028 			fprintf(fp, "{ %jd.%09ld }", (intmax_t)ts.tv_sec,
1029 			    ts.tv_nsec);
1030 		else
1031 			fprintf(fp, "0x%lx", args[sc->offset]);
1032 		break;
1033 	}
1034 	case Timespec2: {
1035 		struct timespec ts[2];
1036 		const char *sep;
1037 		unsigned int i;
1038 
1039 		if (get_struct(pid, (void *)args[sc->offset], &ts, sizeof(ts))
1040 		    != -1) {
1041 			fputs("{ ", fp);
1042 			sep = "";
1043 			for (i = 0; i < nitems(ts); i++) {
1044 				fputs(sep, fp);
1045 				sep = ", ";
1046 				switch (ts[i].tv_nsec) {
1047 				case UTIME_NOW:
1048 					fprintf(fp, "UTIME_NOW");
1049 					break;
1050 				case UTIME_OMIT:
1051 					fprintf(fp, "UTIME_OMIT");
1052 					break;
1053 				default:
1054 					fprintf(fp, "%jd.%09ld",
1055 					    (intmax_t)ts[i].tv_sec,
1056 					    ts[i].tv_nsec);
1057 					break;
1058 				}
1059 			}
1060 			fputs(" }", fp);
1061 		} else
1062 			fprintf(fp, "0x%lx", args[sc->offset]);
1063 		break;
1064 	}
1065 	case Timeval: {
1066 		struct timeval tv;
1067 
1068 		if (get_struct(pid, (void *)args[sc->offset], &tv, sizeof(tv))
1069 		    != -1)
1070 			fprintf(fp, "{ %jd.%06ld }", (intmax_t)tv.tv_sec,
1071 			    tv.tv_usec);
1072 		else
1073 			fprintf(fp, "0x%lx", args[sc->offset]);
1074 		break;
1075 	}
1076 	case Timeval2: {
1077 		struct timeval tv[2];
1078 
1079 		if (get_struct(pid, (void *)args[sc->offset], &tv, sizeof(tv))
1080 		    != -1)
1081 			fprintf(fp, "{ %jd.%06ld, %jd.%06ld }",
1082 			    (intmax_t)tv[0].tv_sec, tv[0].tv_usec,
1083 			    (intmax_t)tv[1].tv_sec, tv[1].tv_usec);
1084 		else
1085 			fprintf(fp, "0x%lx", args[sc->offset]);
1086 		break;
1087 	}
1088 	case Itimerval: {
1089 		struct itimerval itv;
1090 
1091 		if (get_struct(pid, (void *)args[sc->offset], &itv,
1092 		    sizeof(itv)) != -1)
1093 			fprintf(fp, "{ %jd.%06ld, %jd.%06ld }",
1094 			    (intmax_t)itv.it_interval.tv_sec,
1095 			    itv.it_interval.tv_usec,
1096 			    (intmax_t)itv.it_value.tv_sec,
1097 			    itv.it_value.tv_usec);
1098 		else
1099 			fprintf(fp, "0x%lx", args[sc->offset]);
1100 		break;
1101 	}
1102 	case LinuxSockArgs:
1103 	{
1104 		struct linux_socketcall_args largs;
1105 
1106 		if (get_struct(pid, (void *)args[sc->offset], (void *)&largs,
1107 		    sizeof(largs)) != -1)
1108 			fprintf(fp, "{ %s, 0x%lx }",
1109 			    lookup(linux_socketcall_ops, largs.what, 10),
1110 			    (long unsigned int)largs.args);
1111 		else
1112 			fprintf(fp, "0x%lx", args[sc->offset]);
1113 		break;
1114 	}
1115 	case Pollfd: {
1116 		/*
1117 		 * XXX: A Pollfd argument expects the /next/ syscall argument
1118 		 * to be the number of fds in the array. This matches the poll
1119 		 * syscall.
1120 		 */
1121 		struct pollfd *pfd;
1122 		int numfds = args[sc->offset + 1];
1123 		size_t bytes = sizeof(struct pollfd) * numfds;
1124 		int i;
1125 
1126 		if ((pfd = malloc(bytes)) == NULL)
1127 			err(1, "Cannot malloc %zu bytes for pollfd array",
1128 			    bytes);
1129 		if (get_struct(pid, (void *)args[sc->offset], pfd, bytes)
1130 		    != -1) {
1131 			fputs("{", fp);
1132 			for (i = 0; i < numfds; i++) {
1133 				fprintf(fp, " %d/%s", pfd[i].fd,
1134 				    xlookup_bits(poll_flags, pfd[i].events));
1135 			}
1136 			fputs(" }", fp);
1137 		} else {
1138 			fprintf(fp, "0x%lx", args[sc->offset]);
1139 		}
1140 		free(pfd);
1141 		break;
1142 	}
1143 	case Fd_set: {
1144 		/*
1145 		 * XXX: A Fd_set argument expects the /first/ syscall argument
1146 		 * to be the number of fds in the array.  This matches the
1147 		 * select syscall.
1148 		 */
1149 		fd_set *fds;
1150 		int numfds = args[0];
1151 		size_t bytes = _howmany(numfds, _NFDBITS) * _NFDBITS;
1152 		int i;
1153 
1154 		if ((fds = malloc(bytes)) == NULL)
1155 			err(1, "Cannot malloc %zu bytes for fd_set array",
1156 			    bytes);
1157 		if (get_struct(pid, (void *)args[sc->offset], fds, bytes)
1158 		    != -1) {
1159 			fputs("{", fp);
1160 			for (i = 0; i < numfds; i++) {
1161 				if (FD_ISSET(i, fds))
1162 					fprintf(fp, " %d", i);
1163 			}
1164 			fputs(" }", fp);
1165 		} else
1166 			fprintf(fp, "0x%lx", args[sc->offset]);
1167 		free(fds);
1168 		break;
1169 	}
1170 	case Signal:
1171 		fputs(strsig2(args[sc->offset]), fp);
1172 		break;
1173 	case Sigset: {
1174 		long sig;
1175 		sigset_t ss;
1176 		int i, first;
1177 
1178 		sig = args[sc->offset];
1179 		if (get_struct(pid, (void *)args[sc->offset], (void *)&ss,
1180 		    sizeof(ss)) == -1) {
1181 			fprintf(fp, "0x%lx", args[sc->offset]);
1182 			break;
1183 		}
1184 		fputs("{ ", fp);
1185 		first = 1;
1186 		for (i = 1; i < sys_nsig; i++) {
1187 			if (sigismember(&ss, i)) {
1188 				fprintf(fp, "%s%s", !first ? "|" : "",
1189 				    strsig(i));
1190 				first = 0;
1191 			}
1192 		}
1193 		if (!first)
1194 			fputc(' ', fp);
1195 		fputc('}', fp);
1196 		break;
1197 	}
1198 	case Sigprocmask: {
1199 		fputs(xlookup(sigprocmask_ops, args[sc->offset]), fp);
1200 		break;
1201 	}
1202 	case Fcntlflag: {
1203 		/* XXX: Output depends on the value of the previous argument. */
1204 		switch (args[sc->offset - 1]) {
1205 		case F_SETFD:
1206 			fputs(xlookup_bits(fcntlfd_arg, args[sc->offset]), fp);
1207 			break;
1208 		case F_SETFL:
1209 			fputs(xlookup_bits(fcntlfl_arg, args[sc->offset]), fp);
1210 			break;
1211 		case F_GETFD:
1212 		case F_GETFL:
1213 		case F_GETOWN:
1214 			break;
1215 		default:
1216 			fprintf(fp, "0x%lx", args[sc->offset]);
1217 			break;
1218 		}
1219 		break;
1220 	}
1221 	case Open:
1222 		fputs(xlookup_bits(open_flags, args[sc->offset]), fp);
1223 		break;
1224 	case Fcntl:
1225 		fputs(xlookup(fcntl_arg, args[sc->offset]), fp);
1226 		break;
1227 	case Mprot:
1228 		fputs(xlookup_bits(mprot_flags, args[sc->offset]), fp);
1229 		break;
1230 	case Mmapflags: {
1231 		int align, flags;
1232 
1233 		/*
1234 		 * MAP_ALIGNED can't be handled by xlookup_bits(), so
1235 		 * generate that string manually and prepend it to the
1236 		 * string from xlookup_bits().  Have to be careful to
1237 		 * avoid outputting MAP_ALIGNED|0 if MAP_ALIGNED is
1238 		 * the only flag.
1239 		 */
1240 		flags = args[sc->offset] & ~MAP_ALIGNMENT_MASK;
1241 		align = args[sc->offset] & MAP_ALIGNMENT_MASK;
1242 		if (align != 0) {
1243 			if (align == MAP_ALIGNED_SUPER)
1244 				fputs("MAP_ALIGNED_SUPER", fp);
1245 			else
1246 				fprintf(fp, "MAP_ALIGNED(%d)",
1247 				    align >> MAP_ALIGNMENT_SHIFT);
1248 			if (flags == 0)
1249 				break;
1250 			fputc('|', fp);
1251 		}
1252 		fputs(xlookup_bits(mmap_flags, flags), fp);
1253 		break;
1254 	}
1255 	case Whence:
1256 		fputs(xlookup(whence_arg, args[sc->offset]), fp);
1257 		break;
1258 	case Sockdomain:
1259 		fputs(xlookup(sockdomain_arg, args[sc->offset]), fp);
1260 		break;
1261 	case Socktype: {
1262 		int type, flags;
1263 
1264 		flags = args[sc->offset] & (SOCK_CLOEXEC | SOCK_NONBLOCK);
1265 		type = args[sc->offset] & ~flags;
1266 		fputs(xlookup(socktype_arg, type), fp);
1267 		if (flags & SOCK_CLOEXEC)
1268 			fprintf(fp, "|SOCK_CLOEXEC");
1269 		if (flags & SOCK_NONBLOCK)
1270 			fprintf(fp, "|SOCK_NONBLOCK");
1271 		break;
1272 	}
1273 	case Shutdown:
1274 		fputs(xlookup(shutdown_arg, args[sc->offset]), fp);
1275 		break;
1276 	case Resource:
1277 		fputs(xlookup(resource_arg, args[sc->offset]), fp);
1278 		break;
1279 	case Pathconf:
1280 		fputs(xlookup(pathconf_arg, args[sc->offset]), fp);
1281 		break;
1282 	case Rforkflags:
1283 		fputs(xlookup_bits(rfork_flags, args[sc->offset]), fp);
1284 		break;
1285 	case Sockaddr: {
1286 		struct sockaddr_storage ss;
1287 		char addr[64];
1288 		struct sockaddr_in *lsin;
1289 		struct sockaddr_in6 *lsin6;
1290 		struct sockaddr_un *sun;
1291 		struct sockaddr *sa;
1292 		u_char *q;
1293 
1294 		if (args[sc->offset] == 0) {
1295 			fputs("NULL", fp);
1296 			break;
1297 		}
1298 
1299 		/* yuck: get ss_len */
1300 		if (get_struct(pid, (void *)args[sc->offset], (void *)&ss,
1301 		    sizeof(ss.ss_len) + sizeof(ss.ss_family)) == -1) {
1302 			fprintf(fp, "0x%lx", args[sc->offset]);
1303 			break;
1304 		}
1305 
1306 		/*
1307 		 * If ss_len is 0, then try to guess from the sockaddr type.
1308 		 * AF_UNIX may be initialized incorrectly, so always frob
1309 		 * it by using the "right" size.
1310 		 */
1311 		if (ss.ss_len == 0 || ss.ss_family == AF_UNIX) {
1312 			switch (ss.ss_family) {
1313 			case AF_INET:
1314 				ss.ss_len = sizeof(*lsin);
1315 				break;
1316 			case AF_INET6:
1317 				ss.ss_len = sizeof(*lsin6);
1318 				break;
1319 			case AF_UNIX:
1320 				ss.ss_len = sizeof(*sun);
1321 				break;
1322 			default:
1323 				break;
1324 			}
1325 		}
1326 		if (ss.ss_len != 0 &&
1327 		    get_struct(pid, (void *)args[sc->offset], (void *)&ss,
1328 		    ss.ss_len) == -1) {
1329 			fprintf(fp, "0x%lx", args[sc->offset]);
1330 			break;
1331 		}
1332 
1333 		switch (ss.ss_family) {
1334 		case AF_INET:
1335 			lsin = (struct sockaddr_in *)&ss;
1336 			inet_ntop(AF_INET, &lsin->sin_addr, addr, sizeof(addr));
1337 			fprintf(fp, "{ AF_INET %s:%d }", addr,
1338 			    htons(lsin->sin_port));
1339 			break;
1340 		case AF_INET6:
1341 			lsin6 = (struct sockaddr_in6 *)&ss;
1342 			inet_ntop(AF_INET6, &lsin6->sin6_addr, addr,
1343 			    sizeof(addr));
1344 			fprintf(fp, "{ AF_INET6 [%s]:%d }", addr,
1345 			    htons(lsin6->sin6_port));
1346 			break;
1347 		case AF_UNIX:
1348 			sun = (struct sockaddr_un *)&ss;
1349 			fprintf(fp, "{ AF_UNIX \"%s\" }", sun->sun_path);
1350 			break;
1351 		default:
1352 			sa = (struct sockaddr *)&ss;
1353 			fprintf(fp,
1354 			    "{ sa_len = %d, sa_family = %d, sa_data = {",
1355 			    (int)sa->sa_len, (int)sa->sa_family);
1356 			for (q = (u_char *)sa->sa_data;
1357 			     q < (u_char *)sa + sa->sa_len; q++)
1358 				fprintf(fp, "%s 0x%02x",
1359 				    q == (u_char *)sa->sa_data ? "" : ",",
1360 				    *q);
1361 			fputs(" } }", fp);
1362 		}
1363 		break;
1364 	}
1365 	case Sigaction: {
1366 		struct sigaction sa;
1367 
1368 		if (get_struct(pid, (void *)args[sc->offset], &sa, sizeof(sa))
1369 		    != -1) {
1370 			fputs("{ ", fp);
1371 			if (sa.sa_handler == SIG_DFL)
1372 				fputs("SIG_DFL", fp);
1373 			else if (sa.sa_handler == SIG_IGN)
1374 				fputs("SIG_IGN", fp);
1375 			else
1376 				fprintf(fp, "%p", sa.sa_handler);
1377 			fprintf(fp, " %s ss_t }",
1378 			    xlookup_bits(sigaction_flags, sa.sa_flags));
1379 		} else
1380 			fprintf(fp, "0x%lx", args[sc->offset]);
1381 		break;
1382 	}
1383 	case Kevent: {
1384 		/*
1385 		 * XXX XXX: The size of the array is determined by either the
1386 		 * next syscall argument, or by the syscall return value,
1387 		 * depending on which argument number we are.  This matches the
1388 		 * kevent syscall, but luckily that's the only syscall that uses
1389 		 * them.
1390 		 */
1391 		struct kevent *ke;
1392 		int numevents = -1;
1393 		size_t bytes;
1394 		int i;
1395 
1396 		if (sc->offset == 1)
1397 			numevents = args[sc->offset+1];
1398 		else if (sc->offset == 3 && retval != -1)
1399 			numevents = retval;
1400 
1401 		if (numevents >= 0) {
1402 			bytes = sizeof(struct kevent) * numevents;
1403 			if ((ke = malloc(bytes)) == NULL)
1404 				err(1,
1405 				    "Cannot malloc %zu bytes for kevent array",
1406 				    bytes);
1407 		} else
1408 			ke = NULL;
1409 		if (numevents >= 0 && get_struct(pid, (void *)args[sc->offset],
1410 		    ke, bytes) != -1) {
1411 			fputc('{', fp);
1412 			for (i = 0; i < numevents; i++) {
1413 				fputc(' ', fp);
1414 				print_kevent(fp, &ke[i], sc->offset == 1);
1415 			}
1416 			fputs(" }", fp);
1417 		} else {
1418 			fprintf(fp, "0x%lx", args[sc->offset]);
1419 		}
1420 		free(ke);
1421 		break;
1422 	}
1423 	case Stat: {
1424 		struct stat st;
1425 
1426 		if (get_struct(pid, (void *)args[sc->offset], &st, sizeof(st))
1427 		    != -1) {
1428 			char mode[12];
1429 
1430 			strmode(st.st_mode, mode);
1431 			fprintf(fp,
1432 			    "{ mode=%s,inode=%ju,size=%jd,blksize=%ld }", mode,
1433 			    (uintmax_t)st.st_ino, (intmax_t)st.st_size,
1434 			    (long)st.st_blksize);
1435 		} else {
1436 			fprintf(fp, "0x%lx", args[sc->offset]);
1437 		}
1438 		break;
1439 	}
1440 	case Rusage: {
1441 		struct rusage ru;
1442 
1443 		if (get_struct(pid, (void *)args[sc->offset], &ru, sizeof(ru))
1444 		    != -1) {
1445 			fprintf(fp,
1446 			    "{ u=%jd.%06ld,s=%jd.%06ld,in=%ld,out=%ld }",
1447 			    (intmax_t)ru.ru_utime.tv_sec, ru.ru_utime.tv_usec,
1448 			    (intmax_t)ru.ru_stime.tv_sec, ru.ru_stime.tv_usec,
1449 			    ru.ru_inblock, ru.ru_oublock);
1450 		} else
1451 			fprintf(fp, "0x%lx", args[sc->offset]);
1452 		break;
1453 	}
1454 	case Rlimit: {
1455 		struct rlimit rl;
1456 
1457 		if (get_struct(pid, (void *)args[sc->offset], &rl, sizeof(rl))
1458 		    != -1) {
1459 			fprintf(fp, "{ cur=%ju,max=%ju }",
1460 			    rl.rlim_cur, rl.rlim_max);
1461 		} else
1462 			fprintf(fp, "0x%lx", args[sc->offset]);
1463 		break;
1464 	}
1465 	case ExitStatus: {
1466 		int status;
1467 
1468 		if (get_struct(pid, (void *)args[sc->offset], &status,
1469 		    sizeof(status)) != -1) {
1470 			fputs("{ ", fp);
1471 			if (WIFCONTINUED(status))
1472 				fputs("CONTINUED", fp);
1473 			else if (WIFEXITED(status))
1474 				fprintf(fp, "EXITED,val=%d",
1475 				    WEXITSTATUS(status));
1476 			else if (WIFSIGNALED(status))
1477 				fprintf(fp, "SIGNALED,sig=%s%s",
1478 				    strsig2(WTERMSIG(status)),
1479 				    WCOREDUMP(status) ? ",cored" : "");
1480 			else
1481 				fprintf(fp, "STOPPED,sig=%s",
1482 				    strsig2(WTERMSIG(status)));
1483 			fputs(" }", fp);
1484 		} else
1485 			fprintf(fp, "0x%lx", args[sc->offset]);
1486 		break;
1487 	}
1488 	case Waitoptions:
1489 		fputs(xlookup_bits(wait_options, args[sc->offset]), fp);
1490 		break;
1491 	case Idtype:
1492 		fputs(xlookup(idtype_arg, args[sc->offset]), fp);
1493 		break;
1494 	case Procctl:
1495 		fputs(xlookup(procctl_arg, args[sc->offset]), fp);
1496 		break;
1497 	case Umtxop:
1498 		fputs(xlookup(umtx_ops, args[sc->offset]), fp);
1499 		break;
1500 	case Atfd:
1501 		if ((int)args[sc->offset] == AT_FDCWD)
1502 			fputs("AT_FDCWD", fp);
1503 		else
1504 			fprintf(fp, "%d", (int)args[sc->offset]);
1505 		break;
1506 	case Atflags:
1507 		fputs(xlookup_bits(at_flags, args[sc->offset]), fp);
1508 		break;
1509 	case Accessmode:
1510 		if (args[sc->offset] == F_OK)
1511 			fputs("F_OK", fp);
1512 		else
1513 			fputs(xlookup_bits(access_modes, args[sc->offset]), fp);
1514 		break;
1515 	case Sysarch:
1516 		fputs(xlookup(sysarch_ops, args[sc->offset]), fp);
1517 		break;
1518 	default:
1519 		errx(1, "Invalid argument type %d\n", sc->type & ARG_MASK);
1520 	}
1521 	fclose(fp);
1522 	return (tmp);
1523 }
1524 
1525 /*
1526  * Print (to outfile) the system call and its arguments.  Note that
1527  * nargs is the number of arguments (not the number of words; this is
1528  * potentially confusing, I know).
1529  */
1530 void
1531 print_syscall(struct trussinfo *trussinfo, const char *name, int nargs,
1532     char **s_args)
1533 {
1534 	struct timespec timediff;
1535 	int i, len;
1536 
1537 	len = 0;
1538 	if (trussinfo->flags & FOLLOWFORKS)
1539 		len += fprintf(trussinfo->outfile, "%5d: ", trussinfo->pid);
1540 
1541 	if (name != NULL && (strcmp(name, "execve") == 0 ||
1542 	    strcmp(name, "exit") == 0)) {
1543 		clock_gettime(CLOCK_REALTIME, &trussinfo->curthread->after);
1544 	}
1545 
1546 	if (trussinfo->flags & ABSOLUTETIMESTAMPS) {
1547 		timespecsubt(&trussinfo->curthread->after,
1548 		    &trussinfo->start_time, &timediff);
1549 		len += fprintf(trussinfo->outfile, "%jd.%09ld ",
1550 		    (intmax_t)timediff.tv_sec, timediff.tv_nsec);
1551 	}
1552 
1553 	if (trussinfo->flags & RELATIVETIMESTAMPS) {
1554 		timespecsubt(&trussinfo->curthread->after,
1555 		    &trussinfo->curthread->before, &timediff);
1556 		len += fprintf(trussinfo->outfile, "%jd.%09ld ",
1557 		    (intmax_t)timediff.tv_sec, timediff.tv_nsec);
1558 	}
1559 
1560 	len += fprintf(trussinfo->outfile, "%s(", name);
1561 
1562 	for (i = 0; i < nargs; i++) {
1563 		if (s_args[i])
1564 			len += fprintf(trussinfo->outfile, "%s", s_args[i]);
1565 		else
1566 			len += fprintf(trussinfo->outfile,
1567 			    "<missing argument>");
1568 		len += fprintf(trussinfo->outfile, "%s", i < (nargs - 1) ?
1569 		    "," : "");
1570 	}
1571 	len += fprintf(trussinfo->outfile, ")");
1572 	for (i = 0; i < 6 - (len / 8); i++)
1573 		fprintf(trussinfo->outfile, "\t");
1574 }
1575 
1576 void
1577 print_syscall_ret(struct trussinfo *trussinfo, const char *name, int nargs,
1578     char **s_args, int errorp, long retval, struct syscall *sc)
1579 {
1580 	struct timespec timediff;
1581 
1582 	if (trussinfo->flags & COUNTONLY) {
1583 		if (!sc)
1584 			return;
1585 		clock_gettime(CLOCK_REALTIME, &trussinfo->curthread->after);
1586 		timespecsubt(&trussinfo->curthread->after,
1587 		    &trussinfo->curthread->before, &timediff);
1588 		timespecadd(&sc->time, &timediff, &sc->time);
1589 		sc->ncalls++;
1590 		if (errorp)
1591 			sc->nerror++;
1592 		return;
1593 	}
1594 
1595 	print_syscall(trussinfo, name, nargs, s_args);
1596 	fflush(trussinfo->outfile);
1597 	if (errorp)
1598 		fprintf(trussinfo->outfile, " ERR#%ld '%s'\n", retval,
1599 		    strerror(retval));
1600 	else {
1601 		/*
1602 		 * Because pipe(2) has a special assembly glue to provide the
1603 		 * libc API, we have to adjust retval.
1604 		 */
1605 		if (name != NULL && strcmp(name, "pipe") == 0)
1606 			retval = 0;
1607 		fprintf(trussinfo->outfile, " = %ld (0x%lx)\n", retval, retval);
1608 	}
1609 }
1610 
1611 void
1612 print_summary(struct trussinfo *trussinfo)
1613 {
1614 	struct timespec total = {0, 0};
1615 	struct syscall *sc;
1616 	int ncall, nerror;
1617 
1618 	fprintf(trussinfo->outfile, "%-20s%15s%8s%8s\n",
1619 	    "syscall", "seconds", "calls", "errors");
1620 	ncall = nerror = 0;
1621 	for (sc = syscalls; sc->name != NULL; sc++)
1622 		if (sc->ncalls) {
1623 			fprintf(trussinfo->outfile, "%-20s%5jd.%09ld%8d%8d\n",
1624 			    sc->name, (intmax_t)sc->time.tv_sec,
1625 			    sc->time.tv_nsec, sc->ncalls, sc->nerror);
1626 			timespecadd(&total, &sc->time, &total);
1627 			ncall += sc->ncalls;
1628 			nerror += sc->nerror;
1629 		}
1630 	fprintf(trussinfo->outfile, "%20s%15s%8s%8s\n",
1631 	    "", "-------------", "-------", "-------");
1632 	fprintf(trussinfo->outfile, "%-20s%5jd.%09ld%8d%8d\n",
1633 	    "", (intmax_t)total.tv_sec, total.tv_nsec, ncall, nerror);
1634 }
1635