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