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