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