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