xref: /freebsd/usr.bin/truss/syscalls.c (revision 193d9e768ba63fcfb187cfd17f461f7d41345048)
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 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34 
35 /*
36  * This file has routines used to print out system calls and their
37  * arguments.
38  */
39 
40 #include <sys/types.h>
41 #include <sys/event.h>
42 #include <sys/ioccom.h>
43 #include <sys/mount.h>
44 #include <sys/ptrace.h>
45 #include <sys/resource.h>
46 #include <sys/socket.h>
47 #include <sys/stat.h>
48 #include <sys/un.h>
49 #include <sys/wait.h>
50 #include <machine/sysarch.h>
51 #include <netinet/in.h>
52 #include <arpa/inet.h>
53 
54 #include <assert.h>
55 #include <ctype.h>
56 #include <err.h>
57 #include <fcntl.h>
58 #include <poll.h>
59 #include <signal.h>
60 #include <stdbool.h>
61 #include <stdio.h>
62 #include <stdlib.h>
63 #include <string.h>
64 #include <sysdecode.h>
65 #include <unistd.h>
66 #include <vis.h>
67 
68 #include <contrib/cloudabi/cloudabi_types_common.h>
69 
70 #include "truss.h"
71 #include "extern.h"
72 #include "syscall.h"
73 
74 /* 64-bit alignment on 32-bit platforms. */
75 #if !defined(__LP64__) && defined(__powerpc__)
76 #define	QUAD_ALIGN	1
77 #else
78 #define	QUAD_ALIGN	0
79 #endif
80 
81 /* Number of slots needed for a 64-bit argument. */
82 #ifdef __LP64__
83 #define	QUAD_SLOTS	1
84 #else
85 #define	QUAD_SLOTS	2
86 #endif
87 
88 /*
89  * This should probably be in its own file, sorted alphabetically.
90  */
91 static struct syscall decoded_syscalls[] = {
92 	/* Native ABI */
93 	{ .name = "__getcwd", .ret_type = 1, .nargs = 2,
94 	  .args = { { Name | OUT, 0 }, { Int, 1 } } },
95 	{ .name = "_umtx_op", .ret_type = 1, .nargs = 5,
96 	  .args = { { Ptr, 0 }, { Umtxop, 1 }, { LongHex, 2 }, { Ptr, 3 },
97 		    { Ptr, 4 } } },
98 	{ .name = "accept", .ret_type = 1, .nargs = 3,
99 	  .args = { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } },
100 	{ .name = "access", .ret_type = 1, .nargs = 2,
101 	  .args = { { Name | IN, 0 }, { Accessmode, 1 } } },
102 	{ .name = "bind", .ret_type = 1, .nargs = 3,
103 	  .args = { { Int, 0 }, { Sockaddr | IN, 1 }, { Int, 2 } } },
104 	{ .name = "bindat", .ret_type = 1, .nargs = 4,
105 	  .args = { { Atfd, 0 }, { Int, 1 }, { Sockaddr | IN, 2 },
106 		    { Int, 3 } } },
107 	{ .name = "break", .ret_type = 1, .nargs = 1,
108 	  .args = { { Ptr, 0 } } },
109 	{ .name = "chdir", .ret_type = 1, .nargs = 1,
110 	  .args = { { Name, 0 } } },
111 	{ .name = "chflags", .ret_type = 1, .nargs = 2,
112 	  .args = { { Name | IN, 0 }, { Hex, 1 } } },
113 	{ .name = "chmod", .ret_type = 1, .nargs = 2,
114 	  .args = { { Name, 0 }, { Octal, 1 } } },
115 	{ .name = "chown", .ret_type = 1, .nargs = 3,
116 	  .args = { { Name, 0 }, { Int, 1 }, { Int, 2 } } },
117 	{ .name = "chroot", .ret_type = 1, .nargs = 1,
118 	  .args = { { Name, 0 } } },
119 	{ .name = "clock_gettime", .ret_type = 1, .nargs = 2,
120 	  .args = { { Int, 0 }, { Timespec | OUT, 1 } } },
121 	{ .name = "close", .ret_type = 1, .nargs = 1,
122 	  .args = { { Int, 0 } } },
123 	{ .name = "connect", .ret_type = 1, .nargs = 3,
124 	  .args = { { Int, 0 }, { Sockaddr | IN, 1 }, { Int, 2 } } },
125 	{ .name = "connectat", .ret_type = 1, .nargs = 4,
126 	  .args = { { Atfd, 0 }, { Int, 1 }, { Sockaddr | IN, 2 },
127 		    { Int, 3 } } },
128 	{ .name = "eaccess", .ret_type = 1, .nargs = 2,
129 	  .args = { { Name | IN, 0 }, { Accessmode, 1 } } },
130 	{ .name = "execve", .ret_type = 1, .nargs = 3,
131 	  .args = { { Name | IN, 0 }, { ExecArgs | IN, 1 },
132 		    { ExecEnv | IN, 2 } } },
133 	{ .name = "exit", .ret_type = 0, .nargs = 1,
134 	  .args = { { Hex, 0 } } },
135 	{ .name = "faccessat", .ret_type = 1, .nargs = 4,
136 	  .args = { { Atfd, 0 }, { Name | IN, 1 }, { Accessmode, 2 },
137 		    { Atflags, 3 } } },
138 	{ .name = "fchmod", .ret_type = 1, .nargs = 2,
139 	  .args = { { Int, 0 }, { Octal, 1 } } },
140 	{ .name = "fchmodat", .ret_type = 1, .nargs = 4,
141 	  .args = { { Atfd, 0 }, { Name, 1 }, { Octal, 2 }, { Atflags, 3 } } },
142 	{ .name = "fchown", .ret_type = 1, .nargs = 3,
143 	  .args = { { Int, 0 }, { Int, 1 }, { Int, 2 } } },
144 	{ .name = "fchownat", .ret_type = 1, .nargs = 5,
145 	  .args = { { Atfd, 0 }, { Name, 1 }, { Int, 2 }, { Int, 3 },
146 		    { Atflags, 4 } } },
147 	{ .name = "fcntl", .ret_type = 1, .nargs = 3,
148 	  .args = { { Int, 0 }, { Fcntl, 1 }, { Fcntlflag, 2 } } },
149 	{ .name = "fstat", .ret_type = 1, .nargs = 2,
150 	  .args = { { Int, 0 }, { Stat | OUT, 1 } } },
151 	{ .name = "fstatat", .ret_type = 1, .nargs = 4,
152 	  .args = { { Atfd, 0 }, { Name | IN, 1 }, { Stat | OUT, 2 },
153 		    { Atflags, 3 } } },
154 	{ .name = "fstatfs", .ret_type = 1, .nargs = 2,
155 	  .args = { { Int, 0 }, { StatFs | OUT, 1 } } },
156 	{ .name = "ftruncate", .ret_type = 1, .nargs = 2,
157 	  .args = { { Int | IN, 0 }, { QuadHex | IN, 1 + QUAD_ALIGN } } },
158 	{ .name = "futimens", .ret_type = 1, .nargs = 2,
159 	  .args = { { Int, 0 }, { Timespec2 | IN, 1 } } },
160 	{ .name = "futimes", .ret_type = 1, .nargs = 2,
161 	  .args = { { Int, 0 }, { Timeval2 | IN, 1 } } },
162 	{ .name = "futimesat", .ret_type = 1, .nargs = 3,
163 	  .args = { { Atfd, 0 }, { Name | IN, 1 }, { Timeval2 | IN, 2 } } },
164 	{ .name = "getitimer", .ret_type = 1, .nargs = 2,
165 	  .args = { { Int, 0 }, { Itimerval | OUT, 2 } } },
166 	{ .name = "getpeername", .ret_type = 1, .nargs = 3,
167 	  .args = { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } },
168 	{ .name = "getpgid", .ret_type = 1, .nargs = 1,
169 	  .args = { { Int, 0 } } },
170 	{ .name = "getrlimit", .ret_type = 1, .nargs = 2,
171 	  .args = { { Resource, 0 }, { Rlimit | OUT, 1 } } },
172 	{ .name = "getrusage", .ret_type = 1, .nargs = 2,
173 	  .args = { { Int, 0 }, { Rusage | OUT, 1 } } },
174 	{ .name = "getsid", .ret_type = 1, .nargs = 1,
175 	  .args = { { Int, 0 } } },
176 	{ .name = "getsockname", .ret_type = 1, .nargs = 3,
177 	  .args = { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } },
178 	{ .name = "gettimeofday", .ret_type = 1, .nargs = 2,
179 	  .args = { { Timeval | OUT, 0 }, { Ptr, 1 } } },
180 	{ .name = "ioctl", .ret_type = 1, .nargs = 3,
181 	  .args = { { Int, 0 }, { Ioctl, 1 }, { Hex, 2 } } },
182 	{ .name = "kevent", .ret_type = 1, .nargs = 6,
183 	  .args = { { Int, 0 }, { Kevent, 1 }, { Int, 2 }, { Kevent | OUT, 3 },
184 		    { Int, 4 }, { Timespec, 5 } } },
185 	{ .name = "kill", .ret_type = 1, .nargs = 2,
186 	  .args = { { Int | IN, 0 }, { Signal | IN, 1 } } },
187 	{ .name = "kldfind", .ret_type = 1, .nargs = 1,
188 	  .args = { { Name | IN, 0 } } },
189 	{ .name = "kldfirstmod", .ret_type = 1, .nargs = 1,
190 	  .args = { { Int, 0 } } },
191 	{ .name = "kldload", .ret_type = 1, .nargs = 1,
192 	  .args = { { Name | IN, 0 } } },
193 	{ .name = "kldnext", .ret_type = 1, .nargs = 1,
194 	  .args = { { Int, 0 } } },
195 	{ .name = "kldstat", .ret_type = 1, .nargs = 2,
196 	  .args = { { Int, 0 }, { Ptr, 1 } } },
197 	{ .name = "kldunload", .ret_type = 1, .nargs = 1,
198 	  .args = { { Int, 0 } } },
199 	{ .name = "kse_release", .ret_type = 0, .nargs = 1,
200 	  .args = { { Timespec, 0 } } },
201 	{ .name = "lchflags", .ret_type = 1, .nargs = 2,
202 	  .args = { { Name | IN, 0 }, { Hex, 1 } } },
203 	{ .name = "lchmod", .ret_type = 1, .nargs = 2,
204 	  .args = { { Name, 0 }, { Octal, 1 } } },
205 	{ .name = "lchown", .ret_type = 1, .nargs = 3,
206 	  .args = { { Name, 0 }, { Int, 1 }, { Int, 2 } } },
207 	{ .name = "link", .ret_type = 1, .nargs = 2,
208 	  .args = { { Name, 0 }, { Name, 1 } } },
209 	{ .name = "linkat", .ret_type = 1, .nargs = 5,
210 	  .args = { { Atfd, 0 }, { Name, 1 }, { Atfd, 2 }, { Name, 3 },
211 		    { Atflags, 4 } } },
212 	{ .name = "lseek", .ret_type = 2, .nargs = 3,
213 	  .args = { { Int, 0 }, { QuadHex, 1 + QUAD_ALIGN },
214 		    { Whence, 1 + QUAD_SLOTS + QUAD_ALIGN } } },
215 	{ .name = "lstat", .ret_type = 1, .nargs = 2,
216 	  .args = { { Name | IN, 0 }, { Stat | OUT, 1 } } },
217 	{ .name = "lutimes", .ret_type = 1, .nargs = 2,
218 	  .args = { { Name | IN, 0 }, { Timeval2 | IN, 1 } } },
219 	{ .name = "mkdir", .ret_type = 1, .nargs = 2,
220 	  .args = { { Name, 0 }, { Octal, 1 } } },
221 	{ .name = "mkdirat", .ret_type = 1, .nargs = 3,
222 	  .args = { { Atfd, 0 }, { Name, 1 }, { Octal, 2 } } },
223 	{ .name = "mkfifo", .ret_type = 1, .nargs = 2,
224 	  .args = { { Name, 0 }, { Octal, 1 } } },
225 	{ .name = "mkfifoat", .ret_type = 1, .nargs = 3,
226 	  .args = { { Atfd, 0 }, { Name, 1 }, { Octal, 2 } } },
227 	{ .name = "mknod", .ret_type = 1, .nargs = 3,
228 	  .args = { { Name, 0 }, { Octal, 1 }, { Int, 2 } } },
229 	{ .name = "mknodat", .ret_type = 1, .nargs = 4,
230 	  .args = { { Atfd, 0 }, { Name, 1 }, { Octal, 2 }, { Int, 3 } } },
231 	{ .name = "mmap", .ret_type = 1, .nargs = 6,
232 	  .args = { { Ptr, 0 }, { Int, 1 }, { Mprot, 2 }, { Mmapflags, 3 },
233 		    { Int, 4 }, { QuadHex, 5 + QUAD_ALIGN } } },
234 	{ .name = "modfind", .ret_type = 1, .nargs = 1,
235 	  .args = { { Name | IN, 0 } } },
236 	{ .name = "mount", .ret_type = 1, .nargs = 4,
237 	  .args = { { Name, 0 }, { Name, 1 }, { Int, 2 }, { Ptr, 3 } } },
238 	{ .name = "mprotect", .ret_type = 1, .nargs = 3,
239 	  .args = { { Ptr, 0 }, { Int, 1 }, { Mprot, 2 } } },
240 	{ .name = "munmap", .ret_type = 1, .nargs = 2,
241 	  .args = { { Ptr, 0 }, { Int, 1 } } },
242 	{ .name = "nanosleep", .ret_type = 1, .nargs = 1,
243 	  .args = { { Timespec, 0 } } },
244 	{ .name = "open", .ret_type = 1, .nargs = 3,
245 	  .args = { { Name | IN, 0 }, { Open, 1 }, { Octal, 2 } } },
246 	{ .name = "openat", .ret_type = 1, .nargs = 4,
247 	  .args = { { Atfd, 0 }, { Name | IN, 1 }, { Open, 2 },
248 		    { Octal, 3 } } },
249 	{ .name = "pathconf", .ret_type = 1, .nargs = 2,
250 	  .args = { { Name | IN, 0 }, { Pathconf, 1 } } },
251 	{ .name = "pipe", .ret_type = 1, .nargs = 1,
252 	  .args = { { PipeFds | OUT, 0 } } },
253 	{ .name = "pipe2", .ret_type = 1, .nargs = 2,
254 	  .args = { { Ptr, 0 }, { Pipe2, 1 } } },
255 	{ .name = "poll", .ret_type = 1, .nargs = 3,
256 	  .args = { { Pollfd, 0 }, { Int, 1 }, { Int, 2 } } },
257 	{ .name = "posix_openpt", .ret_type = 1, .nargs = 1,
258 	  .args = { { Open, 0 } } },
259 	{ .name = "procctl", .ret_type = 1, .nargs = 4,
260 	  .args = { { Idtype, 0 }, { Quad, 1 + QUAD_ALIGN },
261 		    { Procctl, 1 + QUAD_ALIGN + QUAD_SLOTS },
262 		    { Ptr, 2 + QUAD_ALIGN + QUAD_SLOTS } } },
263 	{ .name = "read", .ret_type = 1, .nargs = 3,
264 	  .args = { { Int, 0 }, { BinString | OUT, 1 }, { Int, 2 } } },
265 	{ .name = "readlink", .ret_type = 1, .nargs = 3,
266 	  .args = { { Name, 0 }, { Readlinkres | OUT, 1 }, { Int, 2 } } },
267 	{ .name = "readlinkat", .ret_type = 1, .nargs = 4,
268 	  .args = { { Atfd, 0 }, { Name, 1 }, { Readlinkres | OUT, 2 },
269 		    { Int, 3 } } },
270 	{ .name = "recvfrom", .ret_type = 1, .nargs = 6,
271 	  .args = { { Int, 0 }, { BinString | OUT, 1 }, { Int, 2 }, { Hex, 3 },
272 		    { Sockaddr | OUT, 4 }, { Ptr | OUT, 5 } } },
273 	{ .name = "rename", .ret_type = 1, .nargs = 2,
274 	  .args = { { Name, 0 }, { Name, 1 } } },
275 	{ .name = "renameat", .ret_type = 1, .nargs = 4,
276 	  .args = { { Atfd, 0 }, { Name, 1 }, { Atfd, 2 }, { Name, 3 } } },
277 	{ .name = "rfork", .ret_type = 1, .nargs = 1,
278 	  .args = { { Rforkflags, 0 } } },
279 	{ .name = "rmdir", .ret_type = 1, .nargs = 1,
280 	  .args = { { Name, 0 } } },
281 	{ .name = "select", .ret_type = 1, .nargs = 5,
282 	  .args = { { Int, 0 }, { Fd_set, 1 }, { Fd_set, 2 }, { Fd_set, 3 },
283 		    { Timeval, 4 } } },
284 	{ .name = "sendto", .ret_type = 1, .nargs = 6,
285 	  .args = { { Int, 0 }, { BinString | IN, 1 }, { Int, 2 }, { Hex, 3 },
286 		    { Sockaddr | IN, 4 }, { Ptr | IN, 5 } } },
287 	{ .name = "setitimer", .ret_type = 1, .nargs = 3,
288 	  .args = { { Int, 0 }, { Itimerval, 1 }, { Itimerval | OUT, 2 } } },
289 	{ .name = "setrlimit", .ret_type = 1, .nargs = 2,
290 	  .args = { { Resource, 0 }, { Rlimit | IN, 1 } } },
291 	{ .name = "shutdown", .ret_type = 1, .nargs = 2,
292 	  .args = { { Int, 0 }, { Shutdown, 1 } } },
293 	{ .name = "sigaction", .ret_type = 1, .nargs = 3,
294 	  .args = { { Signal, 0 }, { Sigaction | IN, 1 },
295 		    { Sigaction | OUT, 2 } } },
296 	{ .name = "sigpending", .ret_type = 1, .nargs = 1,
297 	  .args = { { Sigset | OUT, 0 } } },
298 	{ .name = "sigprocmask", .ret_type = 1, .nargs = 3,
299 	  .args = { { Sigprocmask, 0 }, { Sigset, 1 }, { Sigset | OUT, 2 } } },
300 	{ .name = "sigqueue", .ret_type = 1, .nargs = 3,
301 	  .args = { { Int, 0 }, { Signal, 1 }, { LongHex, 2 } } },
302 	{ .name = "sigreturn", .ret_type = 1, .nargs = 1,
303 	  .args = { { Ptr, 0 } } },
304 	{ .name = "sigsuspend", .ret_type = 1, .nargs = 1,
305 	  .args = { { Sigset | IN, 0 } } },
306 	{ .name = "sigtimedwait", .ret_type = 1, .nargs = 3,
307 	  .args = { { Sigset | IN, 0 }, { Ptr, 1 }, { Timespec | IN, 2 } } },
308 	{ .name = "sigwait", .ret_type = 1, .nargs = 2,
309 	  .args = { { Sigset | IN, 0 }, { Ptr, 1 } } },
310 	{ .name = "sigwaitinfo", .ret_type = 1, .nargs = 2,
311 	  .args = { { Sigset | IN, 0 }, { Ptr, 1 } } },
312 	{ .name = "socket", .ret_type = 1, .nargs = 3,
313 	  .args = { { Sockdomain, 0 }, { Socktype, 1 }, { Int, 2 } } },
314 	{ .name = "stat", .ret_type = 1, .nargs = 2,
315 	  .args = { { Name | IN, 0 }, { Stat | OUT, 1 } } },
316 	{ .name = "statfs", .ret_type = 1, .nargs = 2,
317 	  .args = { { Name | IN, 0 }, { StatFs | OUT, 1 } } },
318 	{ .name = "symlink", .ret_type = 1, .nargs = 2,
319 	  .args = { { Name, 0 }, { Name, 1 } } },
320 	{ .name = "symlinkat", .ret_type = 1, .nargs = 3,
321 	  .args = { { Name, 0 }, { Atfd, 1 }, { Name, 2 } } },
322 	{ .name = "sysarch", .ret_type = 1, .nargs = 2,
323 	  .args = { { Sysarch, 0 }, { Ptr, 1 } } },
324 	{ .name = "thr_kill", .ret_type = 1, .nargs = 2,
325 	  .args = { { Long, 0 }, { Signal, 1 } } },
326 	{ .name = "thr_self", .ret_type = 1, .nargs = 1,
327 	  .args = { { Ptr, 0 } } },
328 	{ .name = "truncate", .ret_type = 1, .nargs = 2,
329 	  .args = { { Name | IN, 0 }, { QuadHex | IN, 1 + QUAD_ALIGN } } },
330 #if 0
331 	/* Does not exist */
332 	{ .name = "umount", .ret_type = 1, .nargs = 2,
333 	  .args = { { Name, 0 }, { Int, 2 } } },
334 #endif
335 	{ .name = "unlink", .ret_type = 1, .nargs = 1,
336 	  .args = { { Name, 0 } } },
337 	{ .name = "unlinkat", .ret_type = 1, .nargs = 3,
338 	  .args = { { Atfd, 0 }, { Name, 1 }, { Atflags, 2 } } },
339 	{ .name = "unmount", .ret_type = 1, .nargs = 2,
340 	  .args = { { Name, 0 }, { Int, 1 } } },
341 	{ .name = "utimensat", .ret_type = 1, .nargs = 4,
342 	  .args = { { Atfd, 0 }, { Name | IN, 1 }, { Timespec2 | IN, 2 },
343 		    { Atflags, 3 } } },
344 	{ .name = "utimes", .ret_type = 1, .nargs = 2,
345 	  .args = { { Name | IN, 0 }, { Timeval2 | IN, 1 } } },
346 	{ .name = "utrace", .ret_type = 1, .nargs = 1,
347 	  .args = { { Utrace, 0 } } },
348 	{ .name = "wait4", .ret_type = 1, .nargs = 4,
349 	  .args = { { Int, 0 }, { ExitStatus | OUT, 1 }, { Waitoptions, 2 },
350 		    { Rusage | OUT, 3 } } },
351 	{ .name = "wait6", .ret_type = 1, .nargs = 6,
352 	  .args = { { Idtype, 0 }, { Quad, 1 + QUAD_ALIGN },
353 		    { ExitStatus | OUT, 1 + QUAD_ALIGN + QUAD_SLOTS },
354 		    { Waitoptions, 2 + QUAD_ALIGN + QUAD_SLOTS },
355 		    { Rusage | OUT, 3 + QUAD_ALIGN + QUAD_SLOTS },
356 		    { Ptr, 4 + QUAD_ALIGN + QUAD_SLOTS } } },
357 	{ .name = "write", .ret_type = 1, .nargs = 3,
358 	  .args = { { Int, 0 }, { BinString | IN, 1 }, { Int, 2 } } },
359 
360 	/* Linux ABI */
361 	{ .name = "linux_access", .ret_type = 1, .nargs = 2,
362 	  .args = { { Name, 0 }, { Accessmode, 1 } } },
363 	{ .name = "linux_execve", .ret_type = 1, .nargs = 3,
364 	  .args = { { Name | IN, 0 }, { ExecArgs | IN, 1 },
365 		    { ExecEnv | IN, 2 } } },
366 	{ .name = "linux_lseek", .ret_type = 2, .nargs = 3,
367 	  .args = { { Int, 0 }, { Int, 1 }, { Whence, 2 } } },
368 	{ .name = "linux_mkdir", .ret_type = 1, .nargs = 2,
369 	  .args = { { Name | IN, 0 }, { Int, 1 } } },
370 	{ .name = "linux_newfstat", .ret_type = 1, .nargs = 2,
371 	  .args = { { Int, 0 }, { Ptr | OUT, 1 } } },
372 	{ .name = "linux_newstat", .ret_type = 1, .nargs = 2,
373 	  .args = { { Name | IN, 0 }, { Ptr | OUT, 1 } } },
374 	{ .name = "linux_open", .ret_type = 1, .nargs = 3,
375 	  .args = { { Name, 0 }, { Hex, 1 }, { Octal, 2 } } },
376 	{ .name = "linux_readlink", .ret_type = 1, .nargs = 3,
377 	  .args = { { Name, 0 }, { Name | OUT, 1 }, { Int, 2 } } },
378 	{ .name = "linux_socketcall", .ret_type = 1, .nargs = 2,
379 	  .args = { { Int, 0 }, { LinuxSockArgs, 1 } } },
380 	{ .name = "linux_stat64", .ret_type = 1, .nargs = 3,
381 	  .args = { { Name | IN, 0 }, { Ptr | OUT, 1 }, { Ptr | IN, 1 } } },
382 
383 	/* CloudABI system calls. */
384 	{ .name = "cloudabi_sys_clock_res_get", .ret_type = 1, .nargs = 1,
385 	  .args = { { CloudABIClockID, 0 } } },
386 	{ .name = "cloudabi_sys_clock_time_get", .ret_type = 1, .nargs = 2,
387 	  .args = { { CloudABIClockID, 0 }, { CloudABITimestamp, 1 } } },
388 	{ .name = "cloudabi_sys_condvar_signal", .ret_type = 1, .nargs = 3,
389 	  .args = { { Ptr, 0 }, { CloudABIMFlags, 1 }, { UInt, 2 } } },
390 	{ .name = "cloudabi_sys_fd_close", .ret_type = 1, .nargs = 1,
391 	  .args = { { Int, 0 } } },
392 	{ .name = "cloudabi_sys_fd_create1", .ret_type = 1, .nargs = 1,
393 	  .args = { { CloudABIFileType, 0 } } },
394 	{ .name = "cloudabi_sys_fd_create2", .ret_type = 1, .nargs = 2,
395 	  .args = { { CloudABIFileType, 0 }, { PipeFds | OUT, 0 } } },
396 	{ .name = "cloudabi_sys_fd_datasync", .ret_type = 1, .nargs = 1,
397 	  .args = { { Int, 0 } } },
398 	{ .name = "cloudabi_sys_fd_dup", .ret_type = 1, .nargs = 1,
399 	  .args = { { Int, 0 } } },
400 	{ .name = "cloudabi_sys_fd_replace", .ret_type = 1, .nargs = 2,
401 	  .args = { { Int, 0 }, { Int, 1 } } },
402 	{ .name = "cloudabi_sys_fd_seek", .ret_type = 1, .nargs = 3,
403 	  .args = { { Int, 0 }, { Int, 1 }, { CloudABIWhence, 2 } } },
404 	{ .name = "cloudabi_sys_fd_stat_get", .ret_type = 1, .nargs = 2,
405 	  .args = { { Int, 0 }, { CloudABIFDStat | OUT, 1 } } },
406 	{ .name = "cloudabi_sys_fd_stat_put", .ret_type = 1, .nargs = 3,
407 	  .args = { { Int, 0 }, { CloudABIFDStat | IN, 1 },
408 	            { ClouduABIFDSFlags, 2 } } },
409 	{ .name = "cloudabi_sys_fd_sync", .ret_type = 1, .nargs = 1,
410 	  .args = { { Int, 0 } } },
411 	{ .name = "cloudabi_sys_file_advise", .ret_type = 1, .nargs = 4,
412 	  .args = { { Int, 0 }, { Int, 1 }, { Int, 2 },
413 	            { CloudABIAdvice, 3 } } },
414 	{ .name = "cloudabi_sys_file_allocate", .ret_type = 1, .nargs = 3,
415 	  .args = { { Int, 0 }, { Int, 1 }, { Int, 2 } } },
416 	{ .name = "cloudabi_sys_file_create", .ret_type = 1, .nargs = 3,
417 	  .args = { { Int, 0 }, { BinString | IN, 1 },
418 	            { CloudABIFileType, 3 } } },
419 	{ .name = "cloudabi_sys_file_link", .ret_type = 1, .nargs = 4,
420 	  .args = { { CloudABILookup, 0 }, { BinString | IN, 1 },
421 	            { Int, 3 }, { BinString | IN, 4 } } },
422 	{ .name = "cloudabi_sys_file_open", .ret_type = 1, .nargs = 4,
423 	  .args = { { Int, 0 }, { BinString | IN, 1 },
424 	            { CloudABIOFlags, 3 }, { CloudABIFDStat | IN, 4 } } },
425 	{ .name = "cloudabi_sys_file_readdir", .ret_type = 1, .nargs = 4,
426 	  .args = { { Int, 0 }, { BinString | OUT, 1 }, { Int, 2 },
427 	            { Int, 3 } } },
428 	{ .name = "cloudabi_sys_file_readlink", .ret_type = 1, .nargs = 4,
429 	  .args = { { Int, 0 }, { BinString | IN, 1 },
430 	            { BinString | OUT, 3 }, { Int, 4 } } },
431 	{ .name = "cloudabi_sys_file_rename", .ret_type = 1, .nargs = 4,
432 	  .args = { { Int, 0 }, { BinString | IN, 1 },
433 	            { Int, 3 }, { BinString | IN, 4 } } },
434 	{ .name = "cloudabi_sys_file_stat_fget", .ret_type = 1, .nargs = 2,
435 	  .args = { { Int, 0 }, { CloudABIFileStat | OUT, 1 } } },
436 	{ .name = "cloudabi_sys_file_stat_fput", .ret_type = 1, .nargs = 3,
437 	  .args = { { Int, 0 }, { CloudABIFileStat | IN, 1 },
438 	            { CloudABIFSFlags, 2 } } },
439 	{ .name = "cloudabi_sys_file_stat_get", .ret_type = 1, .nargs = 3,
440 	  .args = { { CloudABILookup, 0 }, { BinString | IN, 1 },
441 	            { CloudABIFileStat | OUT, 3 } } },
442 	{ .name = "cloudabi_sys_file_stat_put", .ret_type = 1, .nargs = 4,
443 	  .args = { { CloudABILookup, 0 }, { BinString | IN, 1 },
444 	            { CloudABIFileStat | IN, 3 }, { CloudABIFSFlags, 4 } } },
445 	{ .name = "cloudabi_sys_file_symlink", .ret_type = 1, .nargs = 3,
446 	  .args = { { BinString | IN, 0 },
447 	            { Int, 2 }, { BinString | IN, 3 } } },
448 	{ .name = "cloudabi_sys_file_unlink", .ret_type = 1, .nargs = 3,
449 	  .args = { { Int, 0 }, { BinString | IN, 1 },
450 	            { CloudABIULFlags, 3 } } },
451 	{ .name = "cloudabi_sys_lock_unlock", .ret_type = 1, .nargs = 2,
452 	  .args = { { Ptr, 0 }, { CloudABIMFlags, 1 } } },
453 	{ .name = "cloudabi_sys_mem_advise", .ret_type = 1, .nargs = 3,
454 	  .args = { { Ptr, 0 }, { Int, 1 }, { CloudABIAdvice, 2 } } },
455 	{ .name = "cloudabi_sys_mem_lock", .ret_type = 1, .nargs = 2,
456 	  .args = { { Ptr, 0 }, { Int, 1 } } },
457 	{ .name = "cloudabi_sys_mem_map", .ret_type = 1, .nargs = 6,
458 	  .args = { { Ptr, 0 }, { Int, 1 }, { CloudABIMProt, 2 },
459 	            { CloudABIMFlags, 3 }, { Int, 4 }, { Int, 5 } } },
460 	{ .name = "cloudabi_sys_mem_protect", .ret_type = 1, .nargs = 3,
461 	  .args = { { Ptr, 0 }, { Int, 1 }, { CloudABIMProt, 2 } } },
462 	{ .name = "cloudabi_sys_mem_sync", .ret_type = 1, .nargs = 3,
463 	  .args = { { Ptr, 0 }, { Int, 1 }, { CloudABIMSFlags, 2 } } },
464 	{ .name = "cloudabi_sys_mem_unlock", .ret_type = 1, .nargs = 2,
465 	  .args = { { Ptr, 0 }, { Int, 1 } } },
466 	{ .name = "cloudabi_sys_mem_unmap", .ret_type = 1, .nargs = 2,
467 	  .args = { { Ptr, 0 }, { Int, 1 } } },
468 	{ .name = "cloudabi_sys_proc_exec", .ret_type = 1, .nargs = 5,
469 	  .args = { { Int, 0 }, { BinString | IN, 1 }, { Int, 2 },
470 	            { IntArray, 3 }, { Int, 4 } } },
471 	{ .name = "cloudabi_sys_proc_exit", .ret_type = 1, .nargs = 1,
472 	  .args = { { Int, 0 } } },
473 	{ .name = "cloudabi_sys_proc_fork", .ret_type = 1, .nargs = 0 },
474 	{ .name = "cloudabi_sys_proc_raise", .ret_type = 1, .nargs = 1,
475 	  .args = { { CloudABISignal, 0 } } },
476 	{ .name = "cloudabi_sys_random_get", .ret_type = 1, .nargs = 2,
477 	  .args = { { BinString | OUT, 0 }, { Int, 1 } } },
478 	{ .name = "cloudabi_sys_sock_accept", .ret_type = 1, .nargs = 2,
479 	  .args = { { Int, 0 }, { CloudABISockStat | OUT, 1 } } },
480 	{ .name = "cloudabi_sys_sock_bind", .ret_type = 1, .nargs = 3,
481 	  .args = { { Int, 0 }, { Int, 1 }, { BinString | IN, 2 } } },
482 	{ .name = "cloudabi_sys_sock_connect", .ret_type = 1, .nargs = 3,
483 	  .args = { { Int, 0 }, { Int, 1 }, { BinString | IN, 2 } } },
484 	{ .name = "cloudabi_sys_sock_listen", .ret_type = 1, .nargs = 2,
485 	  .args = { { Int, 0 }, { Int, 1 } } },
486 	{ .name = "cloudabi_sys_sock_shutdown", .ret_type = 1, .nargs = 2,
487 	  .args = { { Int, 0 }, { CloudABISDFlags, 1 } } },
488 	{ .name = "cloudabi_sys_sock_stat_get", .ret_type = 1, .nargs = 3,
489 	  .args = { { Int, 0 }, { CloudABISockStat | OUT, 1 },
490 	            { CloudABISSFlags, 2 } } },
491 	{ .name = "cloudabi_sys_thread_exit", .ret_type = 1, .nargs = 2,
492 	  .args = { { Ptr, 0 }, { CloudABIMFlags, 1 } } },
493 	{ .name = "cloudabi_sys_thread_yield", .ret_type = 1, .nargs = 0 },
494 
495 	{ .name = 0 },
496 };
497 static STAILQ_HEAD(, syscall) syscalls;
498 
499 /* Xlat idea taken from strace */
500 struct xlat {
501 	int val;
502 	const char *str;
503 };
504 
505 #define	X(a)	{ a, #a },
506 #define	XEND	{ 0, NULL }
507 
508 static struct xlat kevent_filters[] = {
509 	X(EVFILT_READ) X(EVFILT_WRITE) X(EVFILT_AIO) X(EVFILT_VNODE)
510 	X(EVFILT_PROC) X(EVFILT_SIGNAL) X(EVFILT_TIMER)
511 	X(EVFILT_PROCDESC) X(EVFILT_FS) X(EVFILT_LIO) X(EVFILT_USER)
512 	X(EVFILT_SENDFILE) XEND
513 };
514 
515 static struct xlat kevent_flags[] = {
516 	X(EV_ADD) X(EV_DELETE) X(EV_ENABLE) X(EV_DISABLE) X(EV_ONESHOT)
517 	X(EV_CLEAR) X(EV_RECEIPT) X(EV_DISPATCH) X(EV_FORCEONESHOT)
518 	X(EV_DROP) X(EV_FLAG1) X(EV_ERROR) X(EV_EOF) XEND
519 };
520 
521 static struct xlat kevent_user_ffctrl[] = {
522 	X(NOTE_FFNOP) X(NOTE_FFAND) X(NOTE_FFOR) X(NOTE_FFCOPY)
523 	XEND
524 };
525 
526 static struct xlat kevent_rdwr_fflags[] = {
527 	X(NOTE_LOWAT) X(NOTE_FILE_POLL) XEND
528 };
529 
530 static struct xlat kevent_vnode_fflags[] = {
531 	X(NOTE_DELETE) X(NOTE_WRITE) X(NOTE_EXTEND) X(NOTE_ATTRIB)
532 	X(NOTE_LINK) X(NOTE_RENAME) X(NOTE_REVOKE) XEND
533 };
534 
535 static struct xlat kevent_proc_fflags[] = {
536 	X(NOTE_EXIT) X(NOTE_FORK) X(NOTE_EXEC) X(NOTE_TRACK) X(NOTE_TRACKERR)
537 	X(NOTE_CHILD) XEND
538 };
539 
540 static struct xlat kevent_timer_fflags[] = {
541 	X(NOTE_SECONDS) X(NOTE_MSECONDS) X(NOTE_USECONDS) X(NOTE_NSECONDS)
542 	XEND
543 };
544 
545 static struct xlat poll_flags[] = {
546 	X(POLLSTANDARD) X(POLLIN) X(POLLPRI) X(POLLOUT) X(POLLERR)
547 	X(POLLHUP) X(POLLNVAL) X(POLLRDNORM) X(POLLRDBAND)
548 	X(POLLWRBAND) X(POLLINIGNEOF) XEND
549 };
550 
551 static struct xlat sigaction_flags[] = {
552 	X(SA_ONSTACK) X(SA_RESTART) X(SA_RESETHAND) X(SA_NOCLDSTOP)
553 	X(SA_NODEFER) X(SA_NOCLDWAIT) X(SA_SIGINFO) XEND
554 };
555 
556 static struct xlat pathconf_arg[] = {
557 	X(_PC_LINK_MAX)  X(_PC_MAX_CANON)  X(_PC_MAX_INPUT)
558 	X(_PC_NAME_MAX) X(_PC_PATH_MAX) X(_PC_PIPE_BUF)
559 	X(_PC_CHOWN_RESTRICTED) X(_PC_NO_TRUNC) X(_PC_VDISABLE)
560 	X(_PC_ASYNC_IO) X(_PC_PRIO_IO) X(_PC_SYNC_IO)
561 	X(_PC_ALLOC_SIZE_MIN) X(_PC_FILESIZEBITS)
562 	X(_PC_REC_INCR_XFER_SIZE) X(_PC_REC_MAX_XFER_SIZE)
563 	X(_PC_REC_MIN_XFER_SIZE) X(_PC_REC_XFER_ALIGN)
564 	X(_PC_SYMLINK_MAX) X(_PC_ACL_EXTENDED) X(_PC_ACL_PATH_MAX)
565 	X(_PC_CAP_PRESENT) X(_PC_INF_PRESENT) X(_PC_MAC_PRESENT)
566 	X(_PC_ACL_NFS4) X(_PC_MIN_HOLE_SIZE) XEND
567 };
568 
569 static struct xlat at_flags[] = {
570 	X(AT_EACCESS) X(AT_SYMLINK_NOFOLLOW) X(AT_SYMLINK_FOLLOW)
571 	X(AT_REMOVEDIR) XEND
572 };
573 
574 static struct xlat sysarch_ops[] = {
575 #if defined(__i386__) || defined(__amd64__)
576 	X(I386_GET_LDT) X(I386_SET_LDT) X(I386_GET_IOPERM) X(I386_SET_IOPERM)
577 	X(I386_VM86) X(I386_GET_FSBASE) X(I386_SET_FSBASE) X(I386_GET_GSBASE)
578 	X(I386_SET_GSBASE) X(I386_GET_XFPUSTATE) X(AMD64_GET_FSBASE)
579 	X(AMD64_SET_FSBASE) X(AMD64_GET_GSBASE) X(AMD64_SET_GSBASE)
580 	X(AMD64_GET_XFPUSTATE)
581 #endif
582 	XEND
583 };
584 
585 static struct xlat linux_socketcall_ops[] = {
586 	X(LINUX_SOCKET) X(LINUX_BIND) X(LINUX_CONNECT) X(LINUX_LISTEN)
587 	X(LINUX_ACCEPT) X(LINUX_GETSOCKNAME) X(LINUX_GETPEERNAME)
588 	X(LINUX_SOCKETPAIR) X(LINUX_SEND) X(LINUX_RECV) X(LINUX_SENDTO)
589 	X(LINUX_RECVFROM) X(LINUX_SHUTDOWN) X(LINUX_SETSOCKOPT)
590 	X(LINUX_GETSOCKOPT) X(LINUX_SENDMSG) X(LINUX_RECVMSG)
591 	XEND
592 };
593 
594 #undef X
595 #define	X(a)	{ CLOUDABI_##a, #a },
596 
597 static struct xlat cloudabi_advice[] = {
598 	X(ADVICE_DONTNEED) X(ADVICE_NOREUSE) X(ADVICE_NORMAL)
599 	X(ADVICE_RANDOM) X(ADVICE_SEQUENTIAL) X(ADVICE_WILLNEED)
600 	XEND
601 };
602 
603 static struct xlat cloudabi_clockid[] = {
604 	X(CLOCK_MONOTONIC) X(CLOCK_PROCESS_CPUTIME_ID)
605 	X(CLOCK_REALTIME) X(CLOCK_THREAD_CPUTIME_ID)
606 	XEND
607 };
608 
609 static struct xlat cloudabi_errno[] = {
610 	X(E2BIG) X(EACCES) X(EADDRINUSE) X(EADDRNOTAVAIL)
611 	X(EAFNOSUPPORT) X(EAGAIN) X(EALREADY) X(EBADF) X(EBADMSG)
612 	X(EBUSY) X(ECANCELED) X(ECHILD) X(ECONNABORTED) X(ECONNREFUSED)
613 	X(ECONNRESET) X(EDEADLK) X(EDESTADDRREQ) X(EDOM) X(EDQUOT)
614 	X(EEXIST) X(EFAULT) X(EFBIG) X(EHOSTUNREACH) X(EIDRM) X(EILSEQ)
615 	X(EINPROGRESS) X(EINTR) X(EINVAL) X(EIO) X(EISCONN) X(EISDIR)
616 	X(ELOOP) X(EMFILE) X(EMLINK) X(EMSGSIZE) X(EMULTIHOP)
617 	X(ENAMETOOLONG) X(ENETDOWN) X(ENETRESET) X(ENETUNREACH)
618 	X(ENFILE) X(ENOBUFS) X(ENODEV) X(ENOENT) X(ENOEXEC) X(ENOLCK)
619 	X(ENOLINK) X(ENOMEM) X(ENOMSG) X(ENOPROTOOPT) X(ENOSPC)
620 	X(ENOSYS) X(ENOTCONN) X(ENOTDIR) X(ENOTEMPTY) X(ENOTRECOVERABLE)
621 	X(ENOTSOCK) X(ENOTSUP) X(ENOTTY) X(ENXIO) X(EOVERFLOW)
622 	X(EOWNERDEAD) X(EPERM) X(EPIPE) X(EPROTO) X(EPROTONOSUPPORT)
623 	X(EPROTOTYPE) X(ERANGE) X(EROFS) X(ESPIPE) X(ESRCH) X(ESTALE)
624 	X(ETIMEDOUT) X(ETXTBSY) X(EXDEV) X(ENOTCAPABLE)
625 	XEND
626 };
627 
628 static struct xlat cloudabi_fdflags[] = {
629 	X(FDFLAG_APPEND) X(FDFLAG_DSYNC) X(FDFLAG_NONBLOCK)
630 	X(FDFLAG_RSYNC) X(FDFLAG_SYNC)
631 	XEND
632 };
633 
634 static struct xlat cloudabi_fdsflags[] = {
635 	X(FDSTAT_FLAGS) X(FDSTAT_RIGHTS)
636 	XEND
637 };
638 
639 static struct xlat cloudabi_filetype[] = {
640 	X(FILETYPE_UNKNOWN) X(FILETYPE_BLOCK_DEVICE)
641 	X(FILETYPE_CHARACTER_DEVICE) X(FILETYPE_DIRECTORY)
642 	X(FILETYPE_FIFO) X(FILETYPE_POLL) X(FILETYPE_PROCESS)
643 	X(FILETYPE_REGULAR_FILE) X(FILETYPE_SHARED_MEMORY)
644 	X(FILETYPE_SOCKET_DGRAM) X(FILETYPE_SOCKET_SEQPACKET)
645 	X(FILETYPE_SOCKET_STREAM) X(FILETYPE_SYMBOLIC_LINK)
646 	XEND
647 };
648 
649 static struct xlat cloudabi_fsflags[] = {
650 	X(FILESTAT_ATIM) X(FILESTAT_ATIM_NOW) X(FILESTAT_MTIM)
651 	X(FILESTAT_MTIM_NOW) X(FILESTAT_SIZE)
652 	XEND
653 };
654 
655 static struct xlat cloudabi_mflags[] = {
656 	X(MAP_ANON) X(MAP_FIXED) X(MAP_PRIVATE) X(MAP_SHARED)
657 	XEND
658 };
659 
660 static struct xlat cloudabi_mprot[] = {
661 	X(PROT_EXEC) X(PROT_WRITE) X(PROT_READ)
662 	XEND
663 };
664 
665 static struct xlat cloudabi_msflags[] = {
666 	X(MS_ASYNC) X(MS_INVALIDATE) X(MS_SYNC)
667 	XEND
668 };
669 
670 static struct xlat cloudabi_oflags[] = {
671 	X(O_CREAT) X(O_DIRECTORY) X(O_EXCL) X(O_TRUNC)
672 	XEND
673 };
674 
675 static struct xlat cloudabi_sa_family[] = {
676 	X(AF_UNSPEC) X(AF_INET) X(AF_INET6) X(AF_UNIX)
677 	XEND
678 };
679 
680 static struct xlat cloudabi_sdflags[] = {
681 	X(SHUT_RD) X(SHUT_WR)
682 	XEND
683 };
684 
685 static struct xlat cloudabi_signal[] = {
686 	X(SIGABRT) X(SIGALRM) X(SIGBUS) X(SIGCHLD) X(SIGCONT) X(SIGFPE)
687 	X(SIGHUP) X(SIGILL) X(SIGINT) X(SIGKILL) X(SIGPIPE) X(SIGQUIT)
688 	X(SIGSEGV) X(SIGSTOP) X(SIGSYS) X(SIGTERM) X(SIGTRAP) X(SIGTSTP)
689 	X(SIGTTIN) X(SIGTTOU) X(SIGURG) X(SIGUSR1) X(SIGUSR2)
690 	X(SIGVTALRM) X(SIGXCPU) X(SIGXFSZ)
691 	XEND
692 };
693 
694 static struct xlat cloudabi_ssflags[] = {
695 	X(SOCKSTAT_CLEAR_ERROR)
696 	XEND
697 };
698 
699 static struct xlat cloudabi_ssstate[] = {
700 	X(SOCKSTATE_ACCEPTCONN)
701 	XEND
702 };
703 
704 static struct xlat cloudabi_ulflags[] = {
705 	X(UNLINK_REMOVEDIR)
706 	XEND
707 };
708 
709 static struct xlat cloudabi_whence[] = {
710 	X(WHENCE_CUR) X(WHENCE_END) X(WHENCE_SET)
711 	XEND
712 };
713 
714 #undef X
715 #undef XEND
716 
717 /*
718  * Searches an xlat array for a value, and returns it if found.  Otherwise
719  * return a string representation.
720  */
721 static const char *
722 lookup(struct xlat *xlat, int val, int base)
723 {
724 	static char tmp[16];
725 
726 	for (; xlat->str != NULL; xlat++)
727 		if (xlat->val == val)
728 			return (xlat->str);
729 	switch (base) {
730 		case 8:
731 			sprintf(tmp, "0%o", val);
732 			break;
733 		case 16:
734 			sprintf(tmp, "0x%x", val);
735 			break;
736 		case 10:
737 			sprintf(tmp, "%u", val);
738 			break;
739 		default:
740 			errx(1,"Unknown lookup base");
741 			break;
742 	}
743 	return (tmp);
744 }
745 
746 static const char *
747 xlookup(struct xlat *xlat, int val)
748 {
749 
750 	return (lookup(xlat, val, 16));
751 }
752 
753 /*
754  * Searches an xlat array containing bitfield values.  Remaining bits
755  * set after removing the known ones are printed at the end:
756  * IN|0x400.
757  */
758 static char *
759 xlookup_bits(struct xlat *xlat, int val)
760 {
761 	int len, rem;
762 	static char str[512];
763 
764 	len = 0;
765 	rem = val;
766 	for (; xlat->str != NULL; xlat++) {
767 		if ((xlat->val & rem) == xlat->val) {
768 			/*
769 			 * Don't print the "all-bits-zero" string unless all
770 			 * bits are really zero.
771 			 */
772 			if (xlat->val == 0 && val != 0)
773 				continue;
774 			len += sprintf(str + len, "%s|", xlat->str);
775 			rem &= ~(xlat->val);
776 		}
777 	}
778 
779 	/*
780 	 * If we have leftover bits or didn't match anything, print
781 	 * the remainder.
782 	 */
783 	if (rem || len == 0)
784 		len += sprintf(str + len, "0x%x", rem);
785 	if (len && str[len - 1] == '|')
786 		len--;
787 	str[len] = 0;
788 	return (str);
789 }
790 
791 static void
792 print_integer_arg(const char *(*decoder)(int), FILE *fp, int value)
793 {
794 	const char *str;
795 
796 	str = decoder(value);
797 	if (str != NULL)
798 		fputs(str, fp);
799 	else
800 		fprintf(fp, "%d", value);
801 }
802 
803 static void
804 print_mask_arg(bool (*decoder)(FILE *, int, int *), FILE *fp, int value)
805 {
806 	int rem;
807 
808 	if (!decoder(fp, value, &rem))
809 		fprintf(fp, "0x%x", rem);
810 	else if (rem != 0)
811 		fprintf(fp, "|0x%x", rem);
812 }
813 
814 void
815 init_syscalls(void)
816 {
817 	struct syscall *sc;
818 
819 	STAILQ_INIT(&syscalls);
820 	for (sc = decoded_syscalls; sc->name != NULL; sc++)
821 		STAILQ_INSERT_HEAD(&syscalls, sc, entries);
822 }
823 
824 static struct syscall *
825 find_syscall(struct procabi *abi, u_int number)
826 {
827 	struct extra_syscall *es;
828 
829 	if (number < nitems(abi->syscalls))
830 		return (abi->syscalls[number]);
831 	STAILQ_FOREACH(es, &abi->extra_syscalls, entries) {
832 		if (es->number == number)
833 			return (es->sc);
834 	}
835 	return (NULL);
836 }
837 
838 static void
839 add_syscall(struct procabi *abi, u_int number, struct syscall *sc)
840 {
841 	struct extra_syscall *es;
842 
843 	if (number < nitems(abi->syscalls)) {
844 		assert(abi->syscalls[number] == NULL);
845 		abi->syscalls[number] = sc;
846 	} else {
847 		es = malloc(sizeof(*es));
848 		es->sc = sc;
849 		es->number = number;
850 		STAILQ_INSERT_TAIL(&abi->extra_syscalls, es, entries);
851 	}
852 }
853 
854 /*
855  * If/when the list gets big, it might be desirable to do it
856  * as a hash table or binary search.
857  */
858 struct syscall *
859 get_syscall(struct threadinfo *t, u_int number, u_int nargs)
860 {
861 	struct syscall *sc;
862 	const char *name;
863 	char *new_name;
864 	u_int i;
865 
866 	sc = find_syscall(t->proc->abi, number);
867 	if (sc != NULL)
868 		return (sc);
869 
870 	name = sysdecode_syscallname(t->proc->abi->abi, number);
871 	if (name == NULL) {
872 		asprintf(&new_name, "#%d", number);
873 		name = new_name;
874 	} else
875 		new_name = NULL;
876 	STAILQ_FOREACH(sc, &syscalls, entries) {
877 		if (strcmp(name, sc->name) == 0) {
878 			add_syscall(t->proc->abi, number, sc);
879 			free(new_name);
880 			return (sc);
881 		}
882 	}
883 
884 	/* It is unknown.  Add it into the list. */
885 #if DEBUG
886 	fprintf(stderr, "unknown syscall %s -- setting args to %d\n", name,
887 	    nargs);
888 #endif
889 
890 	sc = calloc(1, sizeof(struct syscall));
891 	sc->name = name;
892 	if (new_name != NULL)
893 		sc->unknown = true;
894 	sc->ret_type = 1;
895 	sc->nargs = nargs;
896 	for (i = 0; i < nargs; i++) {
897 		sc->args[i].offset = i;
898 		/* Treat all unknown arguments as LongHex. */
899 		sc->args[i].type = LongHex;
900 	}
901 	STAILQ_INSERT_HEAD(&syscalls, sc, entries);
902 	add_syscall(t->proc->abi, number, sc);
903 
904 	return (sc);
905 }
906 
907 /*
908  * Copy a fixed amount of bytes from the process.
909  */
910 static int
911 get_struct(pid_t pid, void *offset, void *buf, int len)
912 {
913 	struct ptrace_io_desc iorequest;
914 
915 	iorequest.piod_op = PIOD_READ_D;
916 	iorequest.piod_offs = offset;
917 	iorequest.piod_addr = buf;
918 	iorequest.piod_len = len;
919 	if (ptrace(PT_IO, pid, (caddr_t)&iorequest, 0) < 0)
920 		return (-1);
921 	return (0);
922 }
923 
924 #define	MAXSIZE		4096
925 
926 /*
927  * Copy a string from the process.  Note that it is
928  * expected to be a C string, but if max is set, it will
929  * only get that much.
930  */
931 static char *
932 get_string(pid_t pid, void *addr, int max)
933 {
934 	struct ptrace_io_desc iorequest;
935 	char *buf, *nbuf;
936 	size_t offset, size, totalsize;
937 
938 	offset = 0;
939 	if (max)
940 		size = max + 1;
941 	else {
942 		/* Read up to the end of the current page. */
943 		size = PAGE_SIZE - ((uintptr_t)addr % PAGE_SIZE);
944 		if (size > MAXSIZE)
945 			size = MAXSIZE;
946 	}
947 	totalsize = size;
948 	buf = malloc(totalsize);
949 	if (buf == NULL)
950 		return (NULL);
951 	for (;;) {
952 		iorequest.piod_op = PIOD_READ_D;
953 		iorequest.piod_offs = (char *)addr + offset;
954 		iorequest.piod_addr = buf + offset;
955 		iorequest.piod_len = size;
956 		if (ptrace(PT_IO, pid, (caddr_t)&iorequest, 0) < 0) {
957 			free(buf);
958 			return (NULL);
959 		}
960 		if (memchr(buf + offset, '\0', size) != NULL)
961 			return (buf);
962 		offset += size;
963 		if (totalsize < MAXSIZE && max == 0) {
964 			size = MAXSIZE - totalsize;
965 			if (size > PAGE_SIZE)
966 				size = PAGE_SIZE;
967 			nbuf = realloc(buf, totalsize + size);
968 			if (nbuf == NULL) {
969 				buf[totalsize - 1] = '\0';
970 				return (buf);
971 			}
972 			buf = nbuf;
973 			totalsize += size;
974 		} else {
975 			buf[totalsize - 1] = '\0';
976 			return (buf);
977 		}
978 	}
979 }
980 
981 static const char *
982 strsig2(int sig)
983 {
984 	static char tmp[32];
985 	const char *signame;
986 
987 	signame = sysdecode_signal(sig);
988 	if (signame == NULL) {
989 		snprintf(tmp, sizeof(tmp), "%d", sig);
990 		signame = tmp;
991 	}
992 	return (signame);
993 }
994 
995 static void
996 print_kevent(FILE *fp, struct kevent *ke, int input)
997 {
998 
999 	switch (ke->filter) {
1000 	case EVFILT_READ:
1001 	case EVFILT_WRITE:
1002 	case EVFILT_VNODE:
1003 	case EVFILT_PROC:
1004 	case EVFILT_TIMER:
1005 	case EVFILT_PROCDESC:
1006 		fprintf(fp, "%ju", (uintmax_t)ke->ident);
1007 		break;
1008 	case EVFILT_SIGNAL:
1009 		fputs(strsig2(ke->ident), fp);
1010 		break;
1011 	default:
1012 		fprintf(fp, "%p", (void *)ke->ident);
1013 	}
1014 	fprintf(fp, ",%s,%s,", xlookup(kevent_filters, ke->filter),
1015 	    xlookup_bits(kevent_flags, ke->flags));
1016 	switch (ke->filter) {
1017 	case EVFILT_READ:
1018 	case EVFILT_WRITE:
1019 		fputs(xlookup_bits(kevent_rdwr_fflags, ke->fflags), fp);
1020 		break;
1021 	case EVFILT_VNODE:
1022 		fputs(xlookup_bits(kevent_vnode_fflags, ke->fflags), fp);
1023 		break;
1024 	case EVFILT_PROC:
1025 	case EVFILT_PROCDESC:
1026 		fputs(xlookup_bits(kevent_proc_fflags, ke->fflags), fp);
1027 		break;
1028 	case EVFILT_TIMER:
1029 		fputs(xlookup_bits(kevent_timer_fflags, ke->fflags), fp);
1030 		break;
1031 	case EVFILT_USER: {
1032 		int ctrl, data;
1033 
1034 		ctrl = ke->fflags & NOTE_FFCTRLMASK;
1035 		data = ke->fflags & NOTE_FFLAGSMASK;
1036 		if (input) {
1037 			fputs(xlookup(kevent_user_ffctrl, ctrl), fp);
1038 			if (ke->fflags & NOTE_TRIGGER)
1039 				fputs("|NOTE_TRIGGER", fp);
1040 			if (data != 0)
1041 				fprintf(fp, "|%#x", data);
1042 		} else {
1043 			fprintf(fp, "%#x", data);
1044 		}
1045 		break;
1046 	}
1047 	default:
1048 		fprintf(fp, "%#x", ke->fflags);
1049 	}
1050 	fprintf(fp, ",%p,%p", (void *)ke->data, (void *)ke->udata);
1051 }
1052 
1053 static void
1054 print_utrace(FILE *fp, void *utrace_addr, size_t len)
1055 {
1056 	unsigned char *utrace_buffer;
1057 
1058 	fprintf(fp, "{ ");
1059 	if (sysdecode_utrace(fp, utrace_addr, len)) {
1060 		fprintf(fp, " }");
1061 		return;
1062 	}
1063 
1064 	utrace_buffer = utrace_addr;
1065 	fprintf(fp, "%zu:", len);
1066 	while (len--)
1067 		fprintf(fp, " %02x", *utrace_buffer++);
1068 	fprintf(fp, " }");
1069 }
1070 
1071 /*
1072  * Converts a syscall argument into a string.  Said string is
1073  * allocated via malloc(), so needs to be free()'d.  sc is
1074  * a pointer to the syscall description (see above); args is
1075  * an array of all of the system call arguments.
1076  */
1077 char *
1078 print_arg(struct syscall_args *sc, unsigned long *args, long *retval,
1079     struct trussinfo *trussinfo)
1080 {
1081 	FILE *fp;
1082 	char *tmp;
1083 	size_t tmplen;
1084 	pid_t pid;
1085 
1086 	fp = open_memstream(&tmp, &tmplen);
1087 	pid = trussinfo->curthread->proc->pid;
1088 	switch (sc->type & ARG_MASK) {
1089 	case Hex:
1090 		fprintf(fp, "0x%x", (int)args[sc->offset]);
1091 		break;
1092 	case Octal:
1093 		fprintf(fp, "0%o", (int)args[sc->offset]);
1094 		break;
1095 	case Int:
1096 		fprintf(fp, "%d", (int)args[sc->offset]);
1097 		break;
1098 	case UInt:
1099 		fprintf(fp, "%u", (unsigned int)args[sc->offset]);
1100 		break;
1101 	case LongHex:
1102 		fprintf(fp, "0x%lx", args[sc->offset]);
1103 		break;
1104 	case Long:
1105 		fprintf(fp, "%ld", args[sc->offset]);
1106 		break;
1107 	case Name: {
1108 		/* NULL-terminated string. */
1109 		char *tmp2;
1110 
1111 		tmp2 = get_string(pid, (void*)args[sc->offset], 0);
1112 		fprintf(fp, "\"%s\"", tmp2);
1113 		free(tmp2);
1114 		break;
1115 	}
1116 	case BinString: {
1117 		/*
1118 		 * Binary block of data that might have printable characters.
1119 		 * XXX If type|OUT, assume that the length is the syscall's
1120 		 * return value.  Otherwise, assume that the length of the block
1121 		 * is in the next syscall argument.
1122 		 */
1123 		int max_string = trussinfo->strsize;
1124 		char tmp2[max_string + 1], *tmp3;
1125 		int len;
1126 		int truncated = 0;
1127 
1128 		if (sc->type & OUT)
1129 			len = retval[0];
1130 		else
1131 			len = args[sc->offset + 1];
1132 
1133 		/*
1134 		 * Don't print more than max_string characters, to avoid word
1135 		 * wrap.  If we have to truncate put some ... after the string.
1136 		 */
1137 		if (len > max_string) {
1138 			len = max_string;
1139 			truncated = 1;
1140 		}
1141 		if (len && get_struct(pid, (void*)args[sc->offset], &tmp2, len)
1142 		    != -1) {
1143 			tmp3 = malloc(len * 4 + 1);
1144 			while (len) {
1145 				if (strvisx(tmp3, tmp2, len,
1146 				    VIS_CSTYLE|VIS_TAB|VIS_NL) <= max_string)
1147 					break;
1148 				len--;
1149 				truncated = 1;
1150 			}
1151 			fprintf(fp, "\"%s\"%s", tmp3, truncated ?
1152 			    "..." : "");
1153 			free(tmp3);
1154 		} else {
1155 			fprintf(fp, "0x%lx", args[sc->offset]);
1156 		}
1157 		break;
1158 	}
1159 	case ExecArgs:
1160 	case ExecEnv:
1161 	case StringArray: {
1162 		uintptr_t addr;
1163 		union {
1164 			char *strarray[0];
1165 			char buf[PAGE_SIZE];
1166 		} u;
1167 		char *string;
1168 		size_t len;
1169 		u_int first, i;
1170 
1171 		/*
1172 		 * Only parse argv[] and environment arrays from exec calls
1173 		 * if requested.
1174 		 */
1175 		if (((sc->type & ARG_MASK) == ExecArgs &&
1176 		    (trussinfo->flags & EXECVEARGS) == 0) ||
1177 		    ((sc->type & ARG_MASK) == ExecEnv &&
1178 		    (trussinfo->flags & EXECVEENVS) == 0)) {
1179 			fprintf(fp, "0x%lx", args[sc->offset]);
1180 			break;
1181 		}
1182 
1183 		/*
1184 		 * Read a page of pointers at a time.  Punt if the top-level
1185 		 * pointer is not aligned.  Note that the first read is of
1186 		 * a partial page.
1187 		 */
1188 		addr = args[sc->offset];
1189 		if (addr % sizeof(char *) != 0) {
1190 			fprintf(fp, "0x%lx", args[sc->offset]);
1191 			break;
1192 		}
1193 
1194 		len = PAGE_SIZE - (addr & PAGE_MASK);
1195 		if (get_struct(pid, (void *)addr, u.buf, len) == -1) {
1196 			fprintf(fp, "0x%lx", args[sc->offset]);
1197 			break;
1198 		}
1199 
1200 		fputc('[', fp);
1201 		first = 1;
1202 		i = 0;
1203 		while (u.strarray[i] != NULL) {
1204 			string = get_string(pid, u.strarray[i], 0);
1205 			fprintf(fp, "%s \"%s\"", first ? "" : ",", string);
1206 			free(string);
1207 			first = 0;
1208 
1209 			i++;
1210 			if (i == len / sizeof(char *)) {
1211 				addr += len;
1212 				len = PAGE_SIZE;
1213 				if (get_struct(pid, (void *)addr, u.buf, len) ==
1214 				    -1) {
1215 					fprintf(fp, ", <inval>");
1216 					break;
1217 				}
1218 				i = 0;
1219 			}
1220 		}
1221 		fputs(" ]", fp);
1222 		break;
1223 	}
1224 #ifdef __LP64__
1225 	case Quad:
1226 		fprintf(fp, "%ld", args[sc->offset]);
1227 		break;
1228 	case QuadHex:
1229 		fprintf(fp, "0x%lx", args[sc->offset]);
1230 		break;
1231 #else
1232 	case Quad:
1233 	case QuadHex: {
1234 		unsigned long long ll;
1235 
1236 #if _BYTE_ORDER == _LITTLE_ENDIAN
1237 		ll = (unsigned long long)args[sc->offset + 1] << 32 |
1238 		    args[sc->offset];
1239 #else
1240 		ll = (unsigned long long)args[sc->offset] << 32 |
1241 		    args[sc->offset + 1];
1242 #endif
1243 		if ((sc->type & ARG_MASK) == Quad)
1244 			fprintf(fp, "%lld", ll);
1245 		else
1246 			fprintf(fp, "0x%llx", ll);
1247 		break;
1248 	}
1249 #endif
1250 	case Ptr:
1251 		fprintf(fp, "0x%lx", args[sc->offset]);
1252 		break;
1253 	case Readlinkres: {
1254 		char *tmp2;
1255 
1256 		if (retval[0] == -1)
1257 			break;
1258 		tmp2 = get_string(pid, (void*)args[sc->offset], retval[0]);
1259 		fprintf(fp, "\"%s\"", tmp2);
1260 		free(tmp2);
1261 		break;
1262 	}
1263 	case Ioctl: {
1264 		const char *temp;
1265 		unsigned long cmd;
1266 
1267 		cmd = args[sc->offset];
1268 		temp = sysdecode_ioctlname(cmd);
1269 		if (temp)
1270 			fputs(temp, fp);
1271 		else {
1272 			fprintf(fp, "0x%lx { IO%s%s 0x%lx('%c'), %lu, %lu }",
1273 			    cmd, cmd & IOC_OUT ? "R" : "",
1274 			    cmd & IOC_IN ? "W" : "", IOCGROUP(cmd),
1275 			    isprint(IOCGROUP(cmd)) ? (char)IOCGROUP(cmd) : '?',
1276 			    cmd & 0xFF, IOCPARM_LEN(cmd));
1277 		}
1278 		break;
1279 	}
1280 	case Timespec: {
1281 		struct timespec ts;
1282 
1283 		if (get_struct(pid, (void *)args[sc->offset], &ts,
1284 		    sizeof(ts)) != -1)
1285 			fprintf(fp, "{ %jd.%09ld }", (intmax_t)ts.tv_sec,
1286 			    ts.tv_nsec);
1287 		else
1288 			fprintf(fp, "0x%lx", args[sc->offset]);
1289 		break;
1290 	}
1291 	case Timespec2: {
1292 		struct timespec ts[2];
1293 		const char *sep;
1294 		unsigned int i;
1295 
1296 		if (get_struct(pid, (void *)args[sc->offset], &ts, sizeof(ts))
1297 		    != -1) {
1298 			fputs("{ ", fp);
1299 			sep = "";
1300 			for (i = 0; i < nitems(ts); i++) {
1301 				fputs(sep, fp);
1302 				sep = ", ";
1303 				switch (ts[i].tv_nsec) {
1304 				case UTIME_NOW:
1305 					fprintf(fp, "UTIME_NOW");
1306 					break;
1307 				case UTIME_OMIT:
1308 					fprintf(fp, "UTIME_OMIT");
1309 					break;
1310 				default:
1311 					fprintf(fp, "%jd.%09ld",
1312 					    (intmax_t)ts[i].tv_sec,
1313 					    ts[i].tv_nsec);
1314 					break;
1315 				}
1316 			}
1317 			fputs(" }", fp);
1318 		} else
1319 			fprintf(fp, "0x%lx", args[sc->offset]);
1320 		break;
1321 	}
1322 	case Timeval: {
1323 		struct timeval tv;
1324 
1325 		if (get_struct(pid, (void *)args[sc->offset], &tv, sizeof(tv))
1326 		    != -1)
1327 			fprintf(fp, "{ %jd.%06ld }", (intmax_t)tv.tv_sec,
1328 			    tv.tv_usec);
1329 		else
1330 			fprintf(fp, "0x%lx", args[sc->offset]);
1331 		break;
1332 	}
1333 	case Timeval2: {
1334 		struct timeval tv[2];
1335 
1336 		if (get_struct(pid, (void *)args[sc->offset], &tv, sizeof(tv))
1337 		    != -1)
1338 			fprintf(fp, "{ %jd.%06ld, %jd.%06ld }",
1339 			    (intmax_t)tv[0].tv_sec, tv[0].tv_usec,
1340 			    (intmax_t)tv[1].tv_sec, tv[1].tv_usec);
1341 		else
1342 			fprintf(fp, "0x%lx", args[sc->offset]);
1343 		break;
1344 	}
1345 	case Itimerval: {
1346 		struct itimerval itv;
1347 
1348 		if (get_struct(pid, (void *)args[sc->offset], &itv,
1349 		    sizeof(itv)) != -1)
1350 			fprintf(fp, "{ %jd.%06ld, %jd.%06ld }",
1351 			    (intmax_t)itv.it_interval.tv_sec,
1352 			    itv.it_interval.tv_usec,
1353 			    (intmax_t)itv.it_value.tv_sec,
1354 			    itv.it_value.tv_usec);
1355 		else
1356 			fprintf(fp, "0x%lx", args[sc->offset]);
1357 		break;
1358 	}
1359 	case LinuxSockArgs:
1360 	{
1361 		struct linux_socketcall_args largs;
1362 
1363 		if (get_struct(pid, (void *)args[sc->offset], (void *)&largs,
1364 		    sizeof(largs)) != -1)
1365 			fprintf(fp, "{ %s, 0x%lx }",
1366 			    lookup(linux_socketcall_ops, largs.what, 10),
1367 			    (long unsigned int)largs.args);
1368 		else
1369 			fprintf(fp, "0x%lx", args[sc->offset]);
1370 		break;
1371 	}
1372 	case Pollfd: {
1373 		/*
1374 		 * XXX: A Pollfd argument expects the /next/ syscall argument
1375 		 * to be the number of fds in the array. This matches the poll
1376 		 * syscall.
1377 		 */
1378 		struct pollfd *pfd;
1379 		int numfds = args[sc->offset + 1];
1380 		size_t bytes = sizeof(struct pollfd) * numfds;
1381 		int i;
1382 
1383 		if ((pfd = malloc(bytes)) == NULL)
1384 			err(1, "Cannot malloc %zu bytes for pollfd array",
1385 			    bytes);
1386 		if (get_struct(pid, (void *)args[sc->offset], pfd, bytes)
1387 		    != -1) {
1388 			fputs("{", fp);
1389 			for (i = 0; i < numfds; i++) {
1390 				fprintf(fp, " %d/%s", pfd[i].fd,
1391 				    xlookup_bits(poll_flags, pfd[i].events));
1392 			}
1393 			fputs(" }", fp);
1394 		} else {
1395 			fprintf(fp, "0x%lx", args[sc->offset]);
1396 		}
1397 		free(pfd);
1398 		break;
1399 	}
1400 	case Fd_set: {
1401 		/*
1402 		 * XXX: A Fd_set argument expects the /first/ syscall argument
1403 		 * to be the number of fds in the array.  This matches the
1404 		 * select syscall.
1405 		 */
1406 		fd_set *fds;
1407 		int numfds = args[0];
1408 		size_t bytes = _howmany(numfds, _NFDBITS) * _NFDBITS;
1409 		int i;
1410 
1411 		if ((fds = malloc(bytes)) == NULL)
1412 			err(1, "Cannot malloc %zu bytes for fd_set array",
1413 			    bytes);
1414 		if (get_struct(pid, (void *)args[sc->offset], fds, bytes)
1415 		    != -1) {
1416 			fputs("{", fp);
1417 			for (i = 0; i < numfds; i++) {
1418 				if (FD_ISSET(i, fds))
1419 					fprintf(fp, " %d", i);
1420 			}
1421 			fputs(" }", fp);
1422 		} else
1423 			fprintf(fp, "0x%lx", args[sc->offset]);
1424 		free(fds);
1425 		break;
1426 	}
1427 	case Signal:
1428 		fputs(strsig2(args[sc->offset]), fp);
1429 		break;
1430 	case Sigset: {
1431 		long sig;
1432 		sigset_t ss;
1433 		int i, first;
1434 
1435 		sig = args[sc->offset];
1436 		if (get_struct(pid, (void *)args[sc->offset], (void *)&ss,
1437 		    sizeof(ss)) == -1) {
1438 			fprintf(fp, "0x%lx", args[sc->offset]);
1439 			break;
1440 		}
1441 		fputs("{ ", fp);
1442 		first = 1;
1443 		for (i = 1; i < sys_nsig; i++) {
1444 			if (sigismember(&ss, i)) {
1445 				fprintf(fp, "%s%s", !first ? "|" : "",
1446 				    strsig2(i));
1447 				first = 0;
1448 			}
1449 		}
1450 		if (!first)
1451 			fputc(' ', fp);
1452 		fputc('}', fp);
1453 		break;
1454 	}
1455 	case Sigprocmask:
1456 		print_integer_arg(sysdecode_sigprocmask_how, fp,
1457 		    args[sc->offset]);
1458 		break;
1459 	case Fcntlflag:
1460 		/* XXX: Output depends on the value of the previous argument. */
1461 		if (sysdecode_fcntl_arg_p(args[sc->offset - 1]))
1462 			sysdecode_fcntl_arg(fp, args[sc->offset - 1],
1463 			    args[sc->offset], 16);
1464 		break;
1465 	case Open:
1466 		print_mask_arg(sysdecode_open_flags, fp, args[sc->offset]);
1467 		break;
1468 	case Fcntl:
1469 		print_integer_arg(sysdecode_fcntl_cmd, fp, args[sc->offset]);
1470 		break;
1471 	case Mprot:
1472 		print_mask_arg(sysdecode_mmap_prot, fp, args[sc->offset]);
1473 		break;
1474 	case Mmapflags:
1475 		print_mask_arg(sysdecode_mmap_flags, fp, args[sc->offset]);
1476 		break;
1477 	case Whence:
1478 		print_integer_arg(sysdecode_whence, fp, args[sc->offset]);
1479 		break;
1480 	case Sockdomain:
1481 		print_integer_arg(sysdecode_socketdomain, fp, args[sc->offset]);
1482 		break;
1483 	case Socktype:
1484 		print_mask_arg(sysdecode_socket_type, fp, args[sc->offset]);
1485 		break;
1486 	case Shutdown:
1487 		print_integer_arg(sysdecode_shutdown_how, fp, args[sc->offset]);
1488 		break;
1489 	case Resource:
1490 		print_integer_arg(sysdecode_rlimit, fp, args[sc->offset]);
1491 		break;
1492 	case Pathconf:
1493 		fputs(xlookup(pathconf_arg, args[sc->offset]), fp);
1494 		break;
1495 	case Rforkflags:
1496 		print_mask_arg(sysdecode_rfork_flags, fp, args[sc->offset]);
1497 		break;
1498 	case Sockaddr: {
1499 		char addr[64];
1500 		struct sockaddr_in *lsin;
1501 		struct sockaddr_in6 *lsin6;
1502 		struct sockaddr_un *sun;
1503 		struct sockaddr *sa;
1504 		socklen_t len;
1505 		u_char *q;
1506 
1507 		if (args[sc->offset] == 0) {
1508 			fputs("NULL", fp);
1509 			break;
1510 		}
1511 
1512 		/*
1513 		 * Extract the address length from the next argument.  If
1514 		 * this is an output sockaddr (OUT is set), then the
1515 		 * next argument is a pointer to a socklen_t.  Otherwise
1516 		 * the next argument contains a socklen_t by value.
1517 		 */
1518 		if (sc->type & OUT) {
1519 			if (get_struct(pid, (void *)args[sc->offset + 1],
1520 			    &len, sizeof(len)) == -1) {
1521 				fprintf(fp, "0x%lx", args[sc->offset]);
1522 				break;
1523 			}
1524 		} else
1525 			len = args[sc->offset + 1];
1526 
1527 		/* If the length is too small, just bail. */
1528 		if (len < sizeof(*sa)) {
1529 			fprintf(fp, "0x%lx", args[sc->offset]);
1530 			break;
1531 		}
1532 
1533 		sa = calloc(1, len);
1534 		if (get_struct(pid, (void *)args[sc->offset], sa, len) == -1) {
1535 			free(sa);
1536 			fprintf(fp, "0x%lx", args[sc->offset]);
1537 			break;
1538 		}
1539 
1540 		switch (sa->sa_family) {
1541 		case AF_INET:
1542 			if (len < sizeof(*lsin))
1543 				goto sockaddr_short;
1544 			lsin = (struct sockaddr_in *)(void *)sa;
1545 			inet_ntop(AF_INET, &lsin->sin_addr, addr, sizeof(addr));
1546 			fprintf(fp, "{ AF_INET %s:%d }", addr,
1547 			    htons(lsin->sin_port));
1548 			break;
1549 		case AF_INET6:
1550 			if (len < sizeof(*lsin6))
1551 				goto sockaddr_short;
1552 			lsin6 = (struct sockaddr_in6 *)(void *)sa;
1553 			inet_ntop(AF_INET6, &lsin6->sin6_addr, addr,
1554 			    sizeof(addr));
1555 			fprintf(fp, "{ AF_INET6 [%s]:%d }", addr,
1556 			    htons(lsin6->sin6_port));
1557 			break;
1558 		case AF_UNIX:
1559 			sun = (struct sockaddr_un *)sa;
1560 			fprintf(fp, "{ AF_UNIX \"%.*s\" }",
1561 			    (int)(len - offsetof(struct sockaddr_un, sun_path)),
1562 			    sun->sun_path);
1563 			break;
1564 		default:
1565 		sockaddr_short:
1566 			fprintf(fp,
1567 			    "{ sa_len = %d, sa_family = %d, sa_data = {",
1568 			    (int)sa->sa_len, (int)sa->sa_family);
1569 			for (q = (u_char *)sa->sa_data;
1570 			     q < (u_char *)sa + len; q++)
1571 				fprintf(fp, "%s 0x%02x",
1572 				    q == (u_char *)sa->sa_data ? "" : ",",
1573 				    *q);
1574 			fputs(" } }", fp);
1575 		}
1576 		free(sa);
1577 		break;
1578 	}
1579 	case Sigaction: {
1580 		struct sigaction sa;
1581 
1582 		if (get_struct(pid, (void *)args[sc->offset], &sa, sizeof(sa))
1583 		    != -1) {
1584 			fputs("{ ", fp);
1585 			if (sa.sa_handler == SIG_DFL)
1586 				fputs("SIG_DFL", fp);
1587 			else if (sa.sa_handler == SIG_IGN)
1588 				fputs("SIG_IGN", fp);
1589 			else
1590 				fprintf(fp, "%p", sa.sa_handler);
1591 			fprintf(fp, " %s ss_t }",
1592 			    xlookup_bits(sigaction_flags, sa.sa_flags));
1593 		} else
1594 			fprintf(fp, "0x%lx", args[sc->offset]);
1595 		break;
1596 	}
1597 	case Kevent: {
1598 		/*
1599 		 * XXX XXX: The size of the array is determined by either the
1600 		 * next syscall argument, or by the syscall return value,
1601 		 * depending on which argument number we are.  This matches the
1602 		 * kevent syscall, but luckily that's the only syscall that uses
1603 		 * them.
1604 		 */
1605 		struct kevent *ke;
1606 		int numevents = -1;
1607 		size_t bytes;
1608 		int i;
1609 
1610 		if (sc->offset == 1)
1611 			numevents = args[sc->offset+1];
1612 		else if (sc->offset == 3 && retval[0] != -1)
1613 			numevents = retval[0];
1614 
1615 		if (numevents >= 0) {
1616 			bytes = sizeof(struct kevent) * numevents;
1617 			if ((ke = malloc(bytes)) == NULL)
1618 				err(1,
1619 				    "Cannot malloc %zu bytes for kevent array",
1620 				    bytes);
1621 		} else
1622 			ke = NULL;
1623 		if (numevents >= 0 && get_struct(pid, (void *)args[sc->offset],
1624 		    ke, bytes) != -1) {
1625 			fputc('{', fp);
1626 			for (i = 0; i < numevents; i++) {
1627 				fputc(' ', fp);
1628 				print_kevent(fp, &ke[i], sc->offset == 1);
1629 			}
1630 			fputs(" }", fp);
1631 		} else {
1632 			fprintf(fp, "0x%lx", args[sc->offset]);
1633 		}
1634 		free(ke);
1635 		break;
1636 	}
1637 	case Stat: {
1638 		struct stat st;
1639 
1640 		if (get_struct(pid, (void *)args[sc->offset], &st, sizeof(st))
1641 		    != -1) {
1642 			char mode[12];
1643 
1644 			strmode(st.st_mode, mode);
1645 			fprintf(fp,
1646 			    "{ mode=%s,inode=%ju,size=%jd,blksize=%ld }", mode,
1647 			    (uintmax_t)st.st_ino, (intmax_t)st.st_size,
1648 			    (long)st.st_blksize);
1649 		} else {
1650 			fprintf(fp, "0x%lx", args[sc->offset]);
1651 		}
1652 		break;
1653 	}
1654 	case StatFs: {
1655 		unsigned int i;
1656 		struct statfs buf;
1657 
1658 		if (get_struct(pid, (void *)args[sc->offset], &buf,
1659 		    sizeof(buf)) != -1) {
1660 			char fsid[17];
1661 
1662 			bzero(fsid, sizeof(fsid));
1663 			if (buf.f_fsid.val[0] != 0 || buf.f_fsid.val[1] != 0) {
1664 			        for (i = 0; i < sizeof(buf.f_fsid); i++)
1665 					snprintf(&fsid[i*2],
1666 					    sizeof(fsid) - (i*2), "%02x",
1667 					    ((u_char *)&buf.f_fsid)[i]);
1668 			}
1669 			fprintf(fp,
1670 			    "{ fstypename=%s,mntonname=%s,mntfromname=%s,"
1671 			    "fsid=%s }", buf.f_fstypename, buf.f_mntonname,
1672 			    buf.f_mntfromname, fsid);
1673 		} else
1674 			fprintf(fp, "0x%lx", args[sc->offset]);
1675 		break;
1676 	}
1677 
1678 	case Rusage: {
1679 		struct rusage ru;
1680 
1681 		if (get_struct(pid, (void *)args[sc->offset], &ru, sizeof(ru))
1682 		    != -1) {
1683 			fprintf(fp,
1684 			    "{ u=%jd.%06ld,s=%jd.%06ld,in=%ld,out=%ld }",
1685 			    (intmax_t)ru.ru_utime.tv_sec, ru.ru_utime.tv_usec,
1686 			    (intmax_t)ru.ru_stime.tv_sec, ru.ru_stime.tv_usec,
1687 			    ru.ru_inblock, ru.ru_oublock);
1688 		} else
1689 			fprintf(fp, "0x%lx", args[sc->offset]);
1690 		break;
1691 	}
1692 	case Rlimit: {
1693 		struct rlimit rl;
1694 
1695 		if (get_struct(pid, (void *)args[sc->offset], &rl, sizeof(rl))
1696 		    != -1) {
1697 			fprintf(fp, "{ cur=%ju,max=%ju }",
1698 			    rl.rlim_cur, rl.rlim_max);
1699 		} else
1700 			fprintf(fp, "0x%lx", args[sc->offset]);
1701 		break;
1702 	}
1703 	case ExitStatus: {
1704 		int status;
1705 
1706 		if (get_struct(pid, (void *)args[sc->offset], &status,
1707 		    sizeof(status)) != -1) {
1708 			fputs("{ ", fp);
1709 			if (WIFCONTINUED(status))
1710 				fputs("CONTINUED", fp);
1711 			else if (WIFEXITED(status))
1712 				fprintf(fp, "EXITED,val=%d",
1713 				    WEXITSTATUS(status));
1714 			else if (WIFSIGNALED(status))
1715 				fprintf(fp, "SIGNALED,sig=%s%s",
1716 				    strsig2(WTERMSIG(status)),
1717 				    WCOREDUMP(status) ? ",cored" : "");
1718 			else
1719 				fprintf(fp, "STOPPED,sig=%s",
1720 				    strsig2(WTERMSIG(status)));
1721 			fputs(" }", fp);
1722 		} else
1723 			fprintf(fp, "0x%lx", args[sc->offset]);
1724 		break;
1725 	}
1726 	case Waitoptions:
1727 		print_mask_arg(sysdecode_wait6_options, fp, args[sc->offset]);
1728 		break;
1729 	case Idtype:
1730 		print_integer_arg(sysdecode_idtype, fp, args[sc->offset]);
1731 		break;
1732 	case Procctl:
1733 		print_integer_arg(sysdecode_procctl_cmd, fp, args[sc->offset]);
1734 		break;
1735 	case Umtxop:
1736 		print_integer_arg(sysdecode_umtx_op, fp, args[sc->offset]);
1737 		break;
1738 	case Atfd:
1739 		print_integer_arg(sysdecode_atfd, fp, args[sc->offset]);
1740 		break;
1741 	case Atflags:
1742 		fputs(xlookup_bits(at_flags, args[sc->offset]), fp);
1743 		break;
1744 	case Accessmode:
1745 		print_mask_arg(sysdecode_access_mode, fp, args[sc->offset]);
1746 		break;
1747 	case Sysarch:
1748 		fputs(xlookup(sysarch_ops, args[sc->offset]), fp);
1749 		break;
1750 	case PipeFds:
1751 		/*
1752 		 * The pipe() system call in the kernel returns its
1753 		 * two file descriptors via return values.  However,
1754 		 * the interface exposed by libc is that pipe()
1755 		 * accepts a pointer to an array of descriptors.
1756 		 * Format the output to match the libc API by printing
1757 		 * the returned file descriptors as a fake argument.
1758 		 *
1759 		 * Overwrite the first retval to signal a successful
1760 		 * return as well.
1761 		 */
1762 		fprintf(fp, "{ %ld, %ld }", retval[0], retval[1]);
1763 		retval[0] = 0;
1764 		break;
1765 	case Utrace: {
1766 		size_t len;
1767 		void *utrace_addr;
1768 
1769 		len = args[sc->offset + 1];
1770 		utrace_addr = calloc(1, len);
1771 		if (get_struct(pid, (void *)args[sc->offset],
1772 		    (void *)utrace_addr, len) != -1)
1773 			print_utrace(fp, utrace_addr, len);
1774 		else
1775 			fprintf(fp, "0x%lx", args[sc->offset]);
1776 		free(utrace_addr);
1777 		break;
1778 	}
1779 	case IntArray: {
1780 		int descriptors[16];
1781 		unsigned long i, ndescriptors;
1782 		bool truncated;
1783 
1784 		ndescriptors = args[sc->offset + 1];
1785 		truncated = false;
1786 		if (ndescriptors > nitems(descriptors)) {
1787 			ndescriptors = nitems(descriptors);
1788 			truncated = true;
1789 		}
1790 		if (get_struct(pid, (void *)args[sc->offset],
1791 		    descriptors, ndescriptors * sizeof(descriptors[0])) != -1) {
1792 			fprintf(fp, "{");
1793 			for (i = 0; i < ndescriptors; i++)
1794 				fprintf(fp, i == 0 ? " %d" : ", %d",
1795 				    descriptors[i]);
1796 			fprintf(fp, truncated ? ", ... }" : " }");
1797 		} else
1798 			fprintf(fp, "0x%lx", args[sc->offset]);
1799 		break;
1800 	}
1801 	case Pipe2:
1802 		print_mask_arg(sysdecode_pipe2_flags, fp, args[sc->offset]);
1803 		break;
1804 
1805 	case CloudABIAdvice:
1806 		fputs(xlookup(cloudabi_advice, args[sc->offset]), fp);
1807 		break;
1808 	case CloudABIClockID:
1809 		fputs(xlookup(cloudabi_clockid, args[sc->offset]), fp);
1810 		break;
1811 	case ClouduABIFDSFlags:
1812 		fputs(xlookup_bits(cloudabi_fdsflags, args[sc->offset]), fp);
1813 		break;
1814 	case CloudABIFDStat: {
1815 		cloudabi_fdstat_t fds;
1816 		if (get_struct(pid, (void *)args[sc->offset], &fds, sizeof(fds))
1817 		    != -1) {
1818 			fprintf(fp, "{ %s, ",
1819 			    xlookup(cloudabi_filetype, fds.fs_filetype));
1820 			fprintf(fp, "%s, ... }",
1821 			    xlookup_bits(cloudabi_fdflags, fds.fs_flags));
1822 		} else
1823 			fprintf(fp, "0x%lx", args[sc->offset]);
1824 		break;
1825 	}
1826 	case CloudABIFileStat: {
1827 		cloudabi_filestat_t fsb;
1828 		if (get_struct(pid, (void *)args[sc->offset], &fsb, sizeof(fsb))
1829 		    != -1)
1830 			fprintf(fp, "{ %s, %ju }",
1831 			    xlookup(cloudabi_filetype, fsb.st_filetype),
1832 			    (uintmax_t)fsb.st_size);
1833 		else
1834 			fprintf(fp, "0x%lx", args[sc->offset]);
1835 		break;
1836 	}
1837 	case CloudABIFileType:
1838 		fputs(xlookup(cloudabi_filetype, args[sc->offset]), fp);
1839 		break;
1840 	case CloudABIFSFlags:
1841 		fputs(xlookup_bits(cloudabi_fsflags, args[sc->offset]), fp);
1842 		break;
1843 	case CloudABILookup:
1844 		if ((args[sc->offset] & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) != 0)
1845 			fprintf(fp, "%d|LOOKUP_SYMLINK_FOLLOW",
1846 			    (int)args[sc->offset]);
1847 		else
1848 			fprintf(fp, "%d", (int)args[sc->offset]);
1849 		break;
1850 	case CloudABIMFlags:
1851 		fputs(xlookup_bits(cloudabi_mflags, args[sc->offset]), fp);
1852 		break;
1853 	case CloudABIMProt:
1854 		fputs(xlookup_bits(cloudabi_mprot, args[sc->offset]), fp);
1855 		break;
1856 	case CloudABIMSFlags:
1857 		fputs(xlookup_bits(cloudabi_msflags, args[sc->offset]), fp);
1858 		break;
1859 	case CloudABIOFlags:
1860 		fputs(xlookup_bits(cloudabi_oflags, args[sc->offset]), fp);
1861 		break;
1862 	case CloudABISDFlags:
1863 		fputs(xlookup_bits(cloudabi_sdflags, args[sc->offset]), fp);
1864 		break;
1865 	case CloudABISignal:
1866 		fputs(xlookup(cloudabi_signal, args[sc->offset]), fp);
1867 		break;
1868 	case CloudABISockStat: {
1869 		cloudabi_sockstat_t ss;
1870 		if (get_struct(pid, (void *)args[sc->offset], &ss, sizeof(ss))
1871 		    != -1) {
1872 			fprintf(fp, "{ %s, ", xlookup(
1873 			    cloudabi_sa_family, ss.ss_sockname.sa_family));
1874 			fprintf(fp, "%s, ", xlookup(
1875 			    cloudabi_sa_family, ss.ss_peername.sa_family));
1876 			fprintf(fp, "%s, ", xlookup(
1877 			    cloudabi_errno, ss.ss_error));
1878 			fprintf(fp, "%s }", xlookup_bits(
1879 			    cloudabi_ssstate, ss.ss_state));
1880 		} else
1881 			fprintf(fp, "0x%lx", args[sc->offset]);
1882 		break;
1883 	}
1884 	case CloudABISSFlags:
1885 		fputs(xlookup_bits(cloudabi_ssflags, args[sc->offset]), fp);
1886 		break;
1887 	case CloudABITimestamp:
1888 		fprintf(fp, "%lu.%09lus", args[sc->offset] / 1000000000,
1889 		    args[sc->offset] % 1000000000);
1890 		break;
1891 	case CloudABIULFlags:
1892 		fputs(xlookup_bits(cloudabi_ulflags, args[sc->offset]), fp);
1893 		break;
1894 	case CloudABIWhence:
1895 		fputs(xlookup(cloudabi_whence, args[sc->offset]), fp);
1896 		break;
1897 
1898 	default:
1899 		errx(1, "Invalid argument type %d\n", sc->type & ARG_MASK);
1900 	}
1901 	fclose(fp);
1902 	return (tmp);
1903 }
1904 
1905 /*
1906  * Print (to outfile) the system call and its arguments.
1907  */
1908 void
1909 print_syscall(struct trussinfo *trussinfo)
1910 {
1911 	struct threadinfo *t;
1912 	const char *name;
1913 	char **s_args;
1914 	int i, len, nargs;
1915 
1916 	t = trussinfo->curthread;
1917 
1918 	name = t->cs.sc->name;
1919 	nargs = t->cs.nargs;
1920 	s_args = t->cs.s_args;
1921 
1922 	len = print_line_prefix(trussinfo);
1923 	len += fprintf(trussinfo->outfile, "%s(", name);
1924 
1925 	for (i = 0; i < nargs; i++) {
1926 		if (s_args[i] != NULL)
1927 			len += fprintf(trussinfo->outfile, "%s", s_args[i]);
1928 		else
1929 			len += fprintf(trussinfo->outfile,
1930 			    "<missing argument>");
1931 		len += fprintf(trussinfo->outfile, "%s", i < (nargs - 1) ?
1932 		    "," : "");
1933 	}
1934 	len += fprintf(trussinfo->outfile, ")");
1935 	for (i = 0; i < 6 - (len / 8); i++)
1936 		fprintf(trussinfo->outfile, "\t");
1937 }
1938 
1939 void
1940 print_syscall_ret(struct trussinfo *trussinfo, int errorp, long *retval)
1941 {
1942 	struct timespec timediff;
1943 	struct threadinfo *t;
1944 	struct syscall *sc;
1945 	int error;
1946 
1947 	t = trussinfo->curthread;
1948 	sc = t->cs.sc;
1949 	if (trussinfo->flags & COUNTONLY) {
1950 		timespecsubt(&t->after, &t->before, &timediff);
1951 		timespecadd(&sc->time, &timediff, &sc->time);
1952 		sc->ncalls++;
1953 		if (errorp)
1954 			sc->nerror++;
1955 		return;
1956 	}
1957 
1958 	print_syscall(trussinfo);
1959 	fflush(trussinfo->outfile);
1960 
1961 	if (retval == NULL) {
1962 		/*
1963 		 * This system call resulted in the current thread's exit,
1964 		 * so there is no return value or error to display.
1965 		 */
1966 		fprintf(trussinfo->outfile, "\n");
1967 		return;
1968 	}
1969 
1970 	if (errorp) {
1971 		error = sysdecode_abi_to_freebsd_errno(t->proc->abi->abi,
1972 		    retval[0]);
1973 		fprintf(trussinfo->outfile, " ERR#%ld '%s'\n", retval[0],
1974 		    error == INT_MAX ? "Unknown error" : strerror(error));
1975 	}
1976 #ifndef __LP64__
1977 	else if (sc->ret_type == 2) {
1978 		off_t off;
1979 
1980 #if _BYTE_ORDER == _LITTLE_ENDIAN
1981 		off = (off_t)retval[1] << 32 | retval[0];
1982 #else
1983 		off = (off_t)retval[0] << 32 | retval[1];
1984 #endif
1985 		fprintf(trussinfo->outfile, " = %jd (0x%jx)\n", (intmax_t)off,
1986 		    (intmax_t)off);
1987 	}
1988 #endif
1989 	else
1990 		fprintf(trussinfo->outfile, " = %ld (0x%lx)\n", retval[0],
1991 		    retval[0]);
1992 }
1993 
1994 void
1995 print_summary(struct trussinfo *trussinfo)
1996 {
1997 	struct timespec total = {0, 0};
1998 	struct syscall *sc;
1999 	int ncall, nerror;
2000 
2001 	fprintf(trussinfo->outfile, "%-20s%15s%8s%8s\n",
2002 	    "syscall", "seconds", "calls", "errors");
2003 	ncall = nerror = 0;
2004 	STAILQ_FOREACH(sc, &syscalls, entries)
2005 		if (sc->ncalls) {
2006 			fprintf(trussinfo->outfile, "%-20s%5jd.%09ld%8d%8d\n",
2007 			    sc->name, (intmax_t)sc->time.tv_sec,
2008 			    sc->time.tv_nsec, sc->ncalls, sc->nerror);
2009 			timespecadd(&total, &sc->time, &total);
2010 			ncall += sc->ncalls;
2011 			nerror += sc->nerror;
2012 		}
2013 	fprintf(trussinfo->outfile, "%20s%15s%8s%8s\n",
2014 	    "", "-------------", "-------", "-------");
2015 	fprintf(trussinfo->outfile, "%-20s%5jd.%09ld%8d%8d\n",
2016 	    "", (intmax_t)total.tv_sec, total.tv_nsec, ncall, nerror);
2017 }
2018