syscalls.c revision 255493
1/* 2 * Copyright 1997 Sean Eric Fagan 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 3. All advertising materials mentioning features or use of this software 13 * must display the following acknowledgement: 14 * This product includes software developed by Sean Eric Fagan 15 * 4. Neither the name of the author may be used to endorse or promote 16 * products derived from this software without specific prior written 17 * permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32#ifndef lint 33static const char rcsid[] = 34 "$FreeBSD: head/usr.bin/truss/syscalls.c 255493 2013-09-12 18:08:25Z jhb $"; 35#endif /* not lint */ 36 37/* 38 * This file has routines used to print out system calls and their 39 * arguments. 40 */ 41 42#include <sys/types.h> 43#include <sys/mman.h> 44#include <sys/ptrace.h> 45#include <sys/socket.h> 46#include <sys/time.h> 47#include <sys/un.h> 48#include <sys/wait.h> 49#include <netinet/in.h> 50#include <arpa/inet.h> 51#include <sys/ioccom.h> 52#include <machine/atomic.h> 53#include <errno.h> 54#include <sys/umtx.h> 55#include <sys/event.h> 56#include <sys/stat.h> 57#include <sys/resource.h> 58 59#include <ctype.h> 60#include <err.h> 61#include <fcntl.h> 62#include <poll.h> 63#include <signal.h> 64#include <stdint.h> 65#include <stdio.h> 66#include <stdlib.h> 67#include <string.h> 68#include <time.h> 69#include <unistd.h> 70#include <vis.h> 71 72#include "truss.h" 73#include "extern.h" 74#include "syscall.h" 75 76/* 64-bit alignment on 32-bit platforms. */ 77#ifdef __powerpc__ 78#define QUAD_ALIGN 1 79#else 80#define QUAD_ALIGN 0 81#endif 82 83/* Number of slots needed for a 64-bit argument. */ 84#ifdef __LP64__ 85#define QUAD_SLOTS 1 86#else 87#define QUAD_SLOTS 2 88#endif 89 90/* 91 * This should probably be in its own file, sorted alphabetically. 92 */ 93static struct syscall syscalls[] = { 94 { .name = "fcntl", .ret_type = 1, .nargs = 3, 95 .args = { { Int, 0 } , { Fcntl, 1 }, { Fcntlflag | OUT, 2 } } }, 96 { .name = "fork", .ret_type = 1, .nargs = 0 }, 97 { .name = "vfork", .ret_type = 1, .nargs = 0 }, 98 { .name = "rfork", .ret_type = 1, .nargs = 1, 99 .args = { { Rforkflags, 0 } } }, 100 { .name = "getegid", .ret_type = 1, .nargs = 0 }, 101 { .name = "geteuid", .ret_type = 1, .nargs = 0 }, 102 { .name = "getgid", .ret_type = 1, .nargs = 0 }, 103 { .name = "getpid", .ret_type = 1, .nargs = 0 }, 104 { .name = "getpgid", .ret_type = 1, .nargs = 1, 105 .args = { { Int, 0 } } }, 106 { .name = "getpgrp", .ret_type = 1, .nargs = 0 }, 107 { .name = "getppid", .ret_type = 1, .nargs = 0 }, 108 { .name = "getsid", .ret_type = 1, .nargs = 1, 109 .args = { { Int, 0 } } }, 110 { .name = "getuid", .ret_type = 1, .nargs = 0 }, 111 { .name = "readlink", .ret_type = 1, .nargs = 3, 112 .args = { { Name, 0 } , { Readlinkres | OUT, 1 }, { Int, 2 } } }, 113 { .name = "lseek", .ret_type = 2, .nargs = 3, 114 .args = { { Int, 0 }, { Quad, 1 + QUAD_ALIGN }, { Whence, 1 + QUAD_SLOTS + QUAD_ALIGN } } }, 115 { .name = "linux_lseek", .ret_type = 2, .nargs = 3, 116 .args = { { Int, 0 }, { Int, 1 }, { Whence, 2 } } }, 117 { .name = "mmap", .ret_type = 2, .nargs = 6, 118 .args = { { Ptr, 0 }, { Int, 1 }, { Mprot, 2 }, { Mmapflags, 3 }, { Int, 4 }, { Quad, 5 + QUAD_ALIGN } } }, 119 { .name = "mprotect", .ret_type = 1, .nargs = 3, 120 .args = { { Ptr, 0 }, { Int, 1 }, { Mprot, 2 } } }, 121 { .name = "open", .ret_type = 1, .nargs = 3, 122 .args = { { Name | IN, 0 } , { Open, 1 }, { Octal, 2 } } }, 123 { .name = "mkdir", .ret_type = 1, .nargs = 2, 124 .args = { { Name, 0 } , { Octal, 1 } } }, 125 { .name = "linux_open", .ret_type = 1, .nargs = 3, 126 .args = { { Name, 0 }, { Hex, 1 }, { Octal, 2 } } }, 127 { .name = "close", .ret_type = 1, .nargs = 1, 128 .args = { { Int, 0 } } }, 129 { .name = "link", .ret_type = 0, .nargs = 2, 130 .args = { { Name, 0 }, { Name, 1 } } }, 131 { .name = "unlink", .ret_type = 0, .nargs = 1, 132 .args = { { Name, 0 } } }, 133 { .name = "chdir", .ret_type = 0, .nargs = 1, 134 .args = { { Name, 0 } } }, 135 { .name = "chroot", .ret_type = 0, .nargs = 1, 136 .args = { { Name, 0 } } }, 137 { .name = "mknod", .ret_type = 0, .nargs = 3, 138 .args = { { Name, 0 }, { Octal, 1 }, { Int, 3 } } }, 139 { .name = "chmod", .ret_type = 0, .nargs = 2, 140 .args = { { Name, 0 }, { Octal, 1 } } }, 141 { .name = "chown", .ret_type = 0, .nargs = 3, 142 .args = { { Name, 0 }, { Int, 1 }, { Int, 2 } } }, 143 { .name = "mount", .ret_type = 0, .nargs = 4, 144 .args = { { Name, 0 }, { Name, 1 }, { Int, 2 }, { Ptr, 3 } } }, 145 { .name = "umount", .ret_type = 0, .nargs = 2, 146 .args = { { Name, 0 }, { Int, 2 } } }, 147 { .name = "fstat", .ret_type = 1, .nargs = 2, 148 .args = { { Int, 0 }, { Stat | OUT , 1 } } }, 149 { .name = "stat", .ret_type = 1, .nargs = 2, 150 .args = { { Name | IN, 0 }, { Stat | OUT, 1 } } }, 151 { .name = "lstat", .ret_type = 1, .nargs = 2, 152 .args = { { Name | IN, 0 }, { Stat | OUT, 1 } } }, 153 { .name = "linux_newstat", .ret_type = 1, .nargs = 2, 154 .args = { { Name | IN, 0 }, { Ptr | OUT, 1 } } }, 155 { .name = "linux_newfstat", .ret_type = 1, .nargs = 2, 156 .args = { { Int, 0 }, { Ptr | OUT, 1 } } }, 157 { .name = "write", .ret_type = 1, .nargs = 3, 158 .args = { { Int, 0 }, { BinString | IN, 1 }, { Int, 2 } } }, 159 { .name = "ioctl", .ret_type = 1, .nargs = 3, 160 .args = { { Int, 0 }, { Ioctl, 1 }, { Hex, 2 } } }, 161 { .name = "break", .ret_type = 1, .nargs = 1, 162 .args = { { Ptr, 0 } } }, 163 { .name = "exit", .ret_type = 0, .nargs = 1, 164 .args = { { Hex, 0 } } }, 165 { .name = "access", .ret_type = 1, .nargs = 2, 166 .args = { { Name | IN, 0 }, { Int, 1 } } }, 167 { .name = "sigaction", .ret_type = 1, .nargs = 3, 168 .args = { { Signal, 0 }, { Sigaction | IN, 1 }, { Sigaction | OUT, 2 } } }, 169 { .name = "accept", .ret_type = 1, .nargs = 3, 170 .args = { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } }, 171 { .name = "bind", .ret_type = 1, .nargs = 3, 172 .args = { { Int, 0 }, { Sockaddr | IN, 1 }, { Int, 2 } } }, 173 { .name = "connect", .ret_type = 1, .nargs = 3, 174 .args = { { Int, 0 }, { Sockaddr | IN, 1 }, { Int, 2 } } }, 175 { .name = "getpeername", .ret_type = 1, .nargs = 3, 176 .args = { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } }, 177 { .name = "getsockname", .ret_type = 1, .nargs = 3, 178 .args = { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } }, 179 { .name = "recvfrom", .ret_type = 1, .nargs = 6, 180 .args = { { Int, 0 }, { BinString | OUT, 1 }, { Int, 2 }, { Hex, 3 }, { Sockaddr | OUT, 4 }, { Ptr | OUT, 5 } } }, 181 { .name = "sendto", .ret_type = 1, .nargs = 6, 182 .args = { { Int, 0 }, { BinString | IN, 1 }, { Int, 2 }, { Hex, 3 }, { Sockaddr | IN, 4 }, { Ptr | IN, 5 } } }, 183 { .name = "execve", .ret_type = 1, .nargs = 3, 184 .args = { { Name | IN, 0 }, { StringArray | IN, 1 }, { StringArray | IN, 2 } } }, 185 { .name = "linux_execve", .ret_type = 1, .nargs = 3, 186 .args = { { Name | IN, 0 }, { StringArray | IN, 1 }, { StringArray | IN, 2 } } }, 187 { .name = "kldload", .ret_type = 0, .nargs = 1, 188 .args = { { Name | IN, 0 } } }, 189 { .name = "kldunload", .ret_type = 0, .nargs = 1, 190 .args = { { Int, 0 } } }, 191 { .name = "kldfind", .ret_type = 0, .nargs = 1, 192 .args = { { Name | IN, 0 } } }, 193 { .name = "kldnext", .ret_type = 0, .nargs = 1, 194 .args = { { Int, 0 } } }, 195 { .name = "kldstat", .ret_type = 0, .nargs = 2, 196 .args = { { Int, 0 }, { Ptr, 1 } } }, 197 { .name = "kldfirstmod", .ret_type = 0, .nargs = 1, 198 .args = { { Int, 0 } } }, 199 { .name = "nanosleep", .ret_type = 0, .nargs = 1, 200 .args = { { Timespec, 0 } } }, 201 { .name = "select", .ret_type = 1, .nargs = 5, 202 .args = { { Int, 0 }, { Fd_set, 1 }, { Fd_set, 2 }, { Fd_set, 3 }, { Timeval, 4 } } }, 203 { .name = "poll", .ret_type = 1, .nargs = 3, 204 .args = { { Pollfd, 0 }, { Int, 1 }, { Int, 2 } } }, 205 { .name = "gettimeofday", .ret_type = 1, .nargs = 2, 206 .args = { { Timeval | OUT, 0 }, { Ptr, 1 } } }, 207 { .name = "clock_gettime", .ret_type = 1, .nargs = 2, 208 .args = { { Int, 0 }, { Timespec | OUT, 1 } } }, 209 { .name = "getitimer", .ret_type = 1, .nargs = 2, 210 .args = { { Int, 0 }, { Itimerval | OUT, 2 } } }, 211 { .name = "setitimer", .ret_type = 1, .nargs = 3, 212 .args = { { Int, 0 }, { Itimerval, 1 } , { Itimerval | OUT, 2 } } }, 213 { .name = "kse_release", .ret_type = 0, .nargs = 1, 214 .args = { { Timespec, 0 } } }, 215 { .name = "kevent", .ret_type = 0, .nargs = 6, 216 .args = { { Int, 0 }, { Kevent, 1 }, { Int, 2 }, { Kevent | OUT, 3 }, { Int, 4 }, { Timespec, 5 } } }, 217 { .name = "_umtx_lock", .ret_type = 0, .nargs = 1, 218 .args = { { Umtx, 0 } } }, 219 { .name = "_umtx_unlock", .ret_type = 0, .nargs = 1, 220 .args = { { Umtx, 0 } } }, 221 { .name = "sigprocmask", .ret_type = 0, .nargs = 3, 222 .args = { { Sigprocmask, 0 }, { Sigset, 1 }, { Sigset | OUT, 2 } } }, 223 { .name = "unmount", .ret_type = 1, .nargs = 2, 224 .args = { { Name, 0 }, { Int, 1 } } }, 225 { .name = "socket", .ret_type = 1, .nargs = 3, 226 .args = { { Sockdomain, 0 }, { Socktype, 1 }, { Int, 2 } } }, 227 { .name = "getrusage", .ret_type = 1, .nargs = 2, 228 .args = { { Int, 0 }, { Rusage | OUT, 1 } } }, 229 { .name = "__getcwd", .ret_type = 1, .nargs = 2, 230 .args = { { Name | OUT, 0 }, { Int, 1 } } }, 231 { .name = "shutdown", .ret_type = 1, .nargs = 2, 232 .args = { { Int, 0 }, { Shutdown, 1 } } }, 233 { .name = "getrlimit", .ret_type = 1, .nargs = 2, 234 .args = { { Resource, 0 }, { Rlimit | OUT, 1 } } }, 235 { .name = "setrlimit", .ret_type = 1, .nargs = 2, 236 .args = { { Resource, 0 }, { Rlimit | IN, 1 } } }, 237 { .name = "utimes", .ret_type = 1, .nargs = 2, 238 .args = { { Name | IN, 0 }, { Timeval2 | IN, 1 } } }, 239 { .name = "lutimes", .ret_type = 1, .nargs = 2, 240 .args = { { Name | IN, 0 }, { Timeval2 | IN, 1 } } }, 241 { .name = "futimes", .ret_type = 1, .nargs = 2, 242 .args = { { Int, 0 }, { Timeval | IN, 1 } } }, 243 { .name = "chflags", .ret_type = 1, .nargs = 2, 244 .args = { { Name | IN, 0 }, { Hex, 1 } } }, 245 { .name = "lchflags", .ret_type = 1, .nargs = 2, 246 .args = { { Name | IN, 0 }, { Hex, 1 } } }, 247 { .name = "pathconf", .ret_type = 1, .nargs = 2, 248 .args = { { Name | IN, 0 }, { Pathconf, 1 } } }, 249 { .name = "pipe", .ret_type = 1, .nargs = 1, 250 .args = { { Ptr, 0 } } }, 251 { .name = "truncate", .ret_type = 1, .nargs = 3, 252 .args = { { Name | IN, 0 }, { Int | IN, 1 }, { Quad | IN, 2 } } }, 253 { .name = "ftruncate", .ret_type = 1, .nargs = 3, 254 .args = { { Int | IN, 0 }, { Int | IN, 1 }, { Quad | IN, 2 } } }, 255 { .name = "kill", .ret_type = 1, .nargs = 2, 256 .args = { { Int | IN, 0 }, { Signal | IN, 1 } } }, 257 { .name = "munmap", .ret_type = 1, .nargs = 2, 258 .args = { { Ptr, 0 }, { Int, 1 } } }, 259 { .name = "read", .ret_type = 1, .nargs = 3, 260 .args = { { Int, 0 }, { BinString | OUT, 1 }, { Int, 2 } } }, 261 { .name = "rename", .ret_type = 1, .nargs = 2, 262 .args = { { Name , 0 } , { Name, 1 } } }, 263 { .name = "symlink", .ret_type = 1, .nargs = 2, 264 .args = { { Name , 0 } , { Name, 1 } } }, 265 { .name = "posix_openpt", .ret_type = 1, .nargs = 1, 266 .args = { { Open, 0 } } }, 267 { .name = "wait4", .ret_type = 1, .nargs = 4, 268 .args = { { Int, 0 }, { ExitStatus | OUT, 1 }, { Waitoptions, 2 }, 269 { Rusage | OUT, 3 } } }, 270 { .name = "wait6", .ret_type = 1, .nargs = 6, 271 .args = { { Idtype, 0 }, { Int, 1 }, { ExitStatus | OUT, 2 }, 272 { Waitoptions, 3 }, { Rusage | OUT, 4 }, { Ptr, 5 } } }, 273 { .name = 0 }, 274}; 275 276/* Xlat idea taken from strace */ 277struct xlat { 278 int val; 279 const char *str; 280}; 281 282#define X(a) { a, #a }, 283#define XEND { 0, NULL } 284 285static struct xlat kevent_filters[] = { 286 X(EVFILT_READ) X(EVFILT_WRITE) X(EVFILT_AIO) X(EVFILT_VNODE) 287 X(EVFILT_PROC) X(EVFILT_SIGNAL) X(EVFILT_TIMER) 288 X(EVFILT_FS) X(EVFILT_READ) XEND 289}; 290 291static struct xlat kevent_flags[] = { 292 X(EV_ADD) X(EV_DELETE) X(EV_ENABLE) X(EV_DISABLE) X(EV_ONESHOT) 293 X(EV_CLEAR) X(EV_FLAG1) X(EV_ERROR) X(EV_EOF) XEND 294}; 295 296static struct xlat poll_flags[] = { 297 X(POLLSTANDARD) X(POLLIN) X(POLLPRI) X(POLLOUT) X(POLLERR) 298 X(POLLHUP) X(POLLNVAL) X(POLLRDNORM) X(POLLRDBAND) 299 X(POLLWRBAND) X(POLLINIGNEOF) XEND 300}; 301 302static struct xlat mmap_flags[] = { 303 X(MAP_SHARED) X(MAP_PRIVATE) X(MAP_FIXED) X(MAP_RENAME) 304 X(MAP_NORESERVE) X(MAP_RESERVED0080) X(MAP_RESERVED0100) 305 X(MAP_HASSEMAPHORE) X(MAP_STACK) X(MAP_NOSYNC) X(MAP_ANON) 306 X(MAP_NOCORE) X(MAP_PREFAULT_READ) 307#ifdef MAP_32BIT 308 X(MAP_32BIT) 309#endif 310 XEND 311}; 312 313static struct xlat mprot_flags[] = { 314 X(PROT_NONE) X(PROT_READ) X(PROT_WRITE) X(PROT_EXEC) XEND 315}; 316 317static struct xlat whence_arg[] = { 318 X(SEEK_SET) X(SEEK_CUR) X(SEEK_END) XEND 319}; 320 321static struct xlat sigaction_flags[] = { 322 X(SA_ONSTACK) X(SA_RESTART) X(SA_RESETHAND) X(SA_NOCLDSTOP) 323 X(SA_NODEFER) X(SA_NOCLDWAIT) X(SA_SIGINFO) XEND 324}; 325 326static struct xlat fcntl_arg[] = { 327 X(F_DUPFD) X(F_GETFD) X(F_SETFD) X(F_GETFL) X(F_SETFL) 328 X(F_GETOWN) X(F_SETOWN) X(F_GETLK) X(F_SETLK) X(F_SETLKW) XEND 329}; 330 331static struct xlat fcntlfd_arg[] = { 332 X(FD_CLOEXEC) XEND 333}; 334 335static struct xlat fcntlfl_arg[] = { 336 X(O_APPEND) X(O_ASYNC) X(O_FSYNC) X(O_NONBLOCK) X(O_NOFOLLOW) 337 X(O_DIRECT) XEND 338}; 339 340static struct xlat sockdomain_arg[] = { 341 X(PF_UNSPEC) X(PF_LOCAL) X(PF_UNIX) X(PF_INET) X(PF_IMPLINK) 342 X(PF_PUP) X(PF_CHAOS) X(PF_NETBIOS) X(PF_ISO) X(PF_OSI) 343 X(PF_ECMA) X(PF_DATAKIT) X(PF_CCITT) X(PF_SNA) X(PF_DECnet) 344 X(PF_DLI) X(PF_LAT) X(PF_HYLINK) X(PF_APPLETALK) X(PF_ROUTE) 345 X(PF_LINK) X(PF_XTP) X(PF_COIP) X(PF_CNT) X(PF_SIP) X(PF_IPX) 346 X(PF_RTIP) X(PF_PIP) X(PF_ISDN) X(PF_KEY) X(PF_INET6) 347 X(PF_NATM) X(PF_ATM) X(PF_NETGRAPH) X(PF_SLOW) X(PF_SCLUSTER) 348 X(PF_ARP) X(PF_BLUETOOTH) XEND 349}; 350 351static struct xlat socktype_arg[] = { 352 X(SOCK_STREAM) X(SOCK_DGRAM) X(SOCK_RAW) X(SOCK_RDM) 353 X(SOCK_SEQPACKET) XEND 354}; 355 356static struct xlat open_flags[] = { 357 X(O_RDONLY) X(O_WRONLY) X(O_RDWR) X(O_ACCMODE) X(O_NONBLOCK) 358 X(O_APPEND) X(O_SHLOCK) X(O_EXLOCK) X(O_ASYNC) X(O_FSYNC) 359 X(O_NOFOLLOW) X(O_CREAT) X(O_TRUNC) X(O_EXCL) X(O_NOCTTY) 360 X(O_DIRECT) X(O_DIRECTORY) X(O_EXEC) X(O_TTY_INIT) X(O_CLOEXEC) XEND 361}; 362 363static struct xlat shutdown_arg[] = { 364 X(SHUT_RD) X(SHUT_WR) X(SHUT_RDWR) XEND 365}; 366 367static struct xlat resource_arg[] = { 368 X(RLIMIT_CPU) X(RLIMIT_FSIZE) X(RLIMIT_DATA) X(RLIMIT_STACK) 369 X(RLIMIT_CORE) X(RLIMIT_RSS) X(RLIMIT_MEMLOCK) X(RLIMIT_NPROC) 370 X(RLIMIT_NOFILE) X(RLIMIT_SBSIZE) X(RLIMIT_VMEM) XEND 371}; 372 373static struct xlat pathconf_arg[] = { 374 X(_PC_LINK_MAX) X(_PC_MAX_CANON) X(_PC_MAX_INPUT) 375 X(_PC_NAME_MAX) X(_PC_PATH_MAX) X(_PC_PIPE_BUF) 376 X(_PC_CHOWN_RESTRICTED) X(_PC_NO_TRUNC) X(_PC_VDISABLE) 377 X(_PC_ASYNC_IO) X(_PC_PRIO_IO) X(_PC_SYNC_IO) 378 X(_PC_ALLOC_SIZE_MIN) X(_PC_FILESIZEBITS) 379 X(_PC_REC_INCR_XFER_SIZE) X(_PC_REC_MAX_XFER_SIZE) 380 X(_PC_REC_MIN_XFER_SIZE) X(_PC_REC_XFER_ALIGN) 381 X(_PC_SYMLINK_MAX) X(_PC_ACL_EXTENDED) X(_PC_ACL_PATH_MAX) 382 X(_PC_CAP_PRESENT) X(_PC_INF_PRESENT) X(_PC_MAC_PRESENT) 383 XEND 384}; 385 386static struct xlat rfork_flags[] = { 387 X(RFPROC) X(RFNOWAIT) X(RFFDG) X(RFCFDG) X(RFTHREAD) X(RFMEM) 388 X(RFSIGSHARE) X(RFTSIGZMB) X(RFLINUXTHPN) XEND 389}; 390 391static struct xlat wait_options[] = { 392 X(WNOHANG) X(WUNTRACED) X(WCONTINUED) X(WNOWAIT) X(WEXITED) 393 X(WTRAPPED) XEND 394}; 395 396static struct xlat idtype_arg[] = { 397 X(P_PID) X(P_PPID) X(P_PGID) X(P_SID) X(P_CID) X(P_UID) X(P_GID) 398 X(P_ALL) X(P_LWPID) X(P_TASKID) X(P_PROJID) X(P_POOLID) X(P_JAILID) 399 X(P_CTID) X(P_CPUID) X(P_PSETID) XEND 400}; 401 402#undef X 403#undef XEND 404 405/* 406 * Searches an xlat array for a value, and returns it if found. Otherwise 407 * return a string representation. 408 */ 409static const char * 410lookup(struct xlat *xlat, int val, int base) 411{ 412 static char tmp[16]; 413 414 for (; xlat->str != NULL; xlat++) 415 if (xlat->val == val) 416 return (xlat->str); 417 switch (base) { 418 case 8: 419 sprintf(tmp, "0%o", val); 420 break; 421 case 16: 422 sprintf(tmp, "0x%x", val); 423 break; 424 case 10: 425 sprintf(tmp, "%u", val); 426 break; 427 default: 428 errx(1,"Unknown lookup base"); 429 break; 430 } 431 return (tmp); 432} 433 434static const char * 435xlookup(struct xlat *xlat, int val) 436{ 437 438 return (lookup(xlat, val, 16)); 439} 440 441/* Searches an xlat array containing bitfield values. Remaining bits 442 set after removing the known ones are printed at the end: 443 IN|0x400 */ 444static char * 445xlookup_bits(struct xlat *xlat, int val) 446{ 447 int len, rem; 448 static char str[512]; 449 450 len = 0; 451 rem = val; 452 for (; xlat->str != NULL; xlat++) { 453 if ((xlat->val & rem) == xlat->val) { 454 /* don't print the "all-bits-zero" string unless all 455 bits are really zero */ 456 if (xlat->val == 0 && val != 0) 457 continue; 458 len += sprintf(str + len, "%s|", xlat->str); 459 rem &= ~(xlat->val); 460 } 461 } 462 /* if we have leftover bits or didn't match anything */ 463 if (rem || len == 0) 464 len += sprintf(str + len, "0x%x", rem); 465 if (len && str[len - 1] == '|') 466 len--; 467 str[len] = 0; 468 return (str); 469} 470 471/* 472 * If/when the list gets big, it might be desirable to do it 473 * as a hash table or binary search. 474 */ 475 476struct syscall * 477get_syscall(const char *name) 478{ 479 struct syscall *sc; 480 481 sc = syscalls; 482 if (name == NULL) 483 return (NULL); 484 while (sc->name) { 485 if (strcmp(name, sc->name) == 0) 486 return (sc); 487 sc++; 488 } 489 return (NULL); 490} 491 492/* 493 * get_struct 494 * 495 * Copy a fixed amount of bytes from the process. 496 */ 497 498static int 499get_struct(pid_t pid, void *offset, void *buf, int len) 500{ 501 struct ptrace_io_desc iorequest; 502 503 iorequest.piod_op = PIOD_READ_D; 504 iorequest.piod_offs = offset; 505 iorequest.piod_addr = buf; 506 iorequest.piod_len = len; 507 if (ptrace(PT_IO, pid, (caddr_t)&iorequest, 0) < 0) 508 return (-1); 509 return (0); 510} 511 512#define MAXSIZE 4096 513#define BLOCKSIZE 1024 514/* 515 * get_string 516 * Copy a string from the process. Note that it is 517 * expected to be a C string, but if max is set, it will 518 * only get that much. 519 */ 520 521static char * 522get_string(pid_t pid, void *offset, int max) 523{ 524 struct ptrace_io_desc iorequest; 525 char *buf; 526 int diff, i, size, totalsize; 527 528 diff = 0; 529 totalsize = size = max ? (max + 1) : BLOCKSIZE; 530 buf = malloc(totalsize); 531 if (buf == NULL) 532 return (NULL); 533 for (;;) { 534 diff = totalsize - size; 535 iorequest.piod_op = PIOD_READ_D; 536 iorequest.piod_offs = (char *)offset + diff; 537 iorequest.piod_addr = buf + diff; 538 iorequest.piod_len = size; 539 if (ptrace(PT_IO, pid, (caddr_t)&iorequest, 0) < 0) { 540 free(buf); 541 return (NULL); 542 } 543 for (i = 0 ; i < size; i++) { 544 if (buf[diff + i] == '\0') 545 return (buf); 546 } 547 if (totalsize < MAXSIZE - BLOCKSIZE && max == 0) { 548 totalsize += BLOCKSIZE; 549 buf = realloc(buf, totalsize); 550 size = BLOCKSIZE; 551 } else { 552 buf[totalsize - 1] = '\0'; 553 return (buf); 554 } 555 } 556} 557 558static char * 559strsig2(int sig) 560{ 561 char *tmp; 562 563 tmp = strsig(sig); 564 if (tmp == NULL) 565 asprintf(&tmp, "%d", sig); 566 return (tmp); 567} 568 569/* 570 * print_arg 571 * Converts a syscall argument into a string. Said string is 572 * allocated via malloc(), so needs to be free()'d. The file 573 * descriptor is for the process' memory (via /proc), and is used 574 * to get any data (where the argument is a pointer). sc is 575 * a pointer to the syscall description (see above); args is 576 * an array of all of the system call arguments. 577 */ 578 579char * 580print_arg(struct syscall_args *sc, unsigned long *args, long retval, 581 struct trussinfo *trussinfo) 582{ 583 char *tmp; 584 pid_t pid; 585 586 tmp = NULL; 587 pid = trussinfo->pid; 588 switch (sc->type & ARG_MASK) { 589 case Hex: 590 asprintf(&tmp, "0x%x", (int)args[sc->offset]); 591 break; 592 case Octal: 593 asprintf(&tmp, "0%o", (int)args[sc->offset]); 594 break; 595 case Int: 596 asprintf(&tmp, "%d", (int)args[sc->offset]); 597 break; 598 case Name: { 599 /* NULL-terminated string. */ 600 char *tmp2; 601 tmp2 = get_string(pid, (void*)args[sc->offset], 0); 602 asprintf(&tmp, "\"%s\"", tmp2); 603 free(tmp2); 604 break; 605 } 606 case BinString: { 607 /* Binary block of data that might have printable characters. 608 XXX If type|OUT, assume that the length is the syscall's 609 return value. Otherwise, assume that the length of the block 610 is in the next syscall argument. */ 611 int max_string = trussinfo->strsize; 612 char tmp2[max_string+1], *tmp3; 613 int len; 614 int truncated = 0; 615 616 if (sc->type & OUT) 617 len = retval; 618 else 619 len = args[sc->offset + 1]; 620 621 /* Don't print more than max_string characters, to avoid word 622 wrap. If we have to truncate put some ... after the string. 623 */ 624 if (len > max_string) { 625 len = max_string; 626 truncated = 1; 627 } 628 if (len && get_struct(pid, (void*)args[sc->offset], &tmp2, len) 629 != -1) { 630 tmp3 = malloc(len * 4 + 1); 631 while (len) { 632 if (strvisx(tmp3, tmp2, len, 633 VIS_CSTYLE|VIS_TAB|VIS_NL) <= max_string) 634 break; 635 len--; 636 truncated = 1; 637 }; 638 asprintf(&tmp, "\"%s\"%s", tmp3, truncated ? 639 "..." : ""); 640 free(tmp3); 641 } else { 642 asprintf(&tmp, "0x%lx", args[sc->offset]); 643 } 644 break; 645 } 646 case StringArray: { 647 int num, size, i; 648 char *tmp2; 649 char *string; 650 char *strarray[100]; /* XXX This is ugly. */ 651 652 if (get_struct(pid, (void *)args[sc->offset], 653 (void *)&strarray, sizeof(strarray)) == -1) 654 err(1, "get_struct %p", (void *)args[sc->offset]); 655 num = 0; 656 size = 0; 657 658 /* Find out how large of a buffer we'll need. */ 659 while (strarray[num] != NULL) { 660 string = get_string(pid, (void*)strarray[num], 0); 661 size += strlen(string); 662 free(string); 663 num++; 664 } 665 size += 4 + (num * 4); 666 tmp = (char *)malloc(size); 667 tmp2 = tmp; 668 669 tmp2 += sprintf(tmp2, " ["); 670 for (i = 0; i < num; i++) { 671 string = get_string(pid, (void*)strarray[i], 0); 672 tmp2 += sprintf(tmp2, " \"%s\"%c", string, 673 (i + 1 == num) ? ' ' : ','); 674 free(string); 675 } 676 tmp2 += sprintf(tmp2, "]"); 677 break; 678 } 679#ifdef __LP64__ 680 case Quad: 681 asprintf(&tmp, "0x%lx", args[sc->offset]); 682 break; 683#else 684 case Quad: { 685 unsigned long long ll; 686 ll = *(unsigned long long *)(args + sc->offset); 687 asprintf(&tmp, "0x%llx", ll); 688 break; 689 } 690#endif 691 case Ptr: 692 asprintf(&tmp, "0x%lx", args[sc->offset]); 693 break; 694 case Readlinkres: { 695 char *tmp2; 696 if (retval == -1) { 697 tmp = strdup(""); 698 break; 699 } 700 tmp2 = get_string(pid, (void*)args[sc->offset], retval); 701 asprintf(&tmp, "\"%s\"", tmp2); 702 free(tmp2); 703 break; 704 } 705 case Ioctl: { 706 const char *temp = ioctlname(args[sc->offset]); 707 if (temp) 708 tmp = strdup(temp); 709 else { 710 unsigned long arg = args[sc->offset]; 711 asprintf(&tmp, "0x%lx { IO%s%s 0x%lx('%c'), %lu, %lu }", 712 arg, arg & IOC_OUT ? "R" : "", 713 arg & IOC_IN ? "W" : "", IOCGROUP(arg), 714 isprint(IOCGROUP(arg)) ? (char)IOCGROUP(arg) : '?', 715 arg & 0xFF, IOCPARM_LEN(arg)); 716 } 717 break; 718 } 719 case Umtx: { 720 struct umtx umtx; 721 if (get_struct(pid, (void *)args[sc->offset], &umtx, 722 sizeof(umtx)) != -1) 723 asprintf(&tmp, "{ 0x%lx }", (long)umtx.u_owner); 724 else 725 asprintf(&tmp, "0x%lx", args[sc->offset]); 726 break; 727 } 728 case Timespec: { 729 struct timespec ts; 730 if (get_struct(pid, (void *)args[sc->offset], &ts, 731 sizeof(ts)) != -1) 732 asprintf(&tmp, "{%ld.%09ld }", (long)ts.tv_sec, 733 ts.tv_nsec); 734 else 735 asprintf(&tmp, "0x%lx", args[sc->offset]); 736 break; 737 } 738 case Timeval: { 739 struct timeval tv; 740 if (get_struct(pid, (void *)args[sc->offset], &tv, sizeof(tv)) 741 != -1) 742 asprintf(&tmp, "{%ld.%06ld }", (long)tv.tv_sec, 743 tv.tv_usec); 744 else 745 asprintf(&tmp, "0x%lx", args[sc->offset]); 746 break; 747 } 748 case Timeval2: { 749 struct timeval tv[2]; 750 if (get_struct(pid, (void *)args[sc->offset], &tv, sizeof(tv)) 751 != -1) 752 asprintf(&tmp, "{%ld.%06ld, %ld.%06ld }", 753 (long)tv[0].tv_sec, tv[0].tv_usec, 754 (long)tv[1].tv_sec, tv[1].tv_usec); 755 else 756 asprintf(&tmp, "0x%lx", args[sc->offset]); 757 break; 758 } 759 case Itimerval: { 760 struct itimerval itv; 761 if (get_struct(pid, (void *)args[sc->offset], &itv, 762 sizeof(itv)) != -1) 763 asprintf(&tmp, "{%ld.%06ld, %ld.%06ld }", 764 (long)itv.it_interval.tv_sec, 765 itv.it_interval.tv_usec, 766 (long)itv.it_value.tv_sec, 767 itv.it_value.tv_usec); 768 else 769 asprintf(&tmp, "0x%lx", args[sc->offset]); 770 break; 771 } 772 case Pollfd: { 773 /* 774 * XXX: A Pollfd argument expects the /next/ syscall argument 775 * to be the number of fds in the array. This matches the poll 776 * syscall. 777 */ 778 struct pollfd *pfd; 779 int numfds = args[sc->offset+1]; 780 int bytes = sizeof(struct pollfd) * numfds; 781 int i, tmpsize, u, used; 782 const int per_fd = 100; 783 784 if ((pfd = malloc(bytes)) == NULL) 785 err(1, "Cannot malloc %d bytes for pollfd array", 786 bytes); 787 if (get_struct(pid, (void *)args[sc->offset], pfd, bytes) 788 != -1) { 789 used = 0; 790 tmpsize = 1 + per_fd * numfds + 2; 791 if ((tmp = malloc(tmpsize)) == NULL) 792 err(1, "Cannot alloc %d bytes for poll output", 793 tmpsize); 794 795 tmp[used++] = '{'; 796 for (i = 0; i < numfds; i++) { 797 798 u = snprintf(tmp + used, per_fd, "%s%d/%s", 799 i > 0 ? " " : "", pfd[i].fd, 800 xlookup_bits(poll_flags, pfd[i].events)); 801 if (u > 0) 802 used += u < per_fd ? u : per_fd; 803 } 804 tmp[used++] = '}'; 805 tmp[used++] = '\0'; 806 } else { 807 asprintf(&tmp, "0x%lx", args[sc->offset]); 808 } 809 free(pfd); 810 break; 811 } 812 case Fd_set: { 813 /* 814 * XXX: A Fd_set argument expects the /first/ syscall argument 815 * to be the number of fds in the array. This matches the 816 * select syscall. 817 */ 818 fd_set *fds; 819 int numfds = args[0]; 820 int bytes = _howmany(numfds, _NFDBITS) * _NFDBITS; 821 int i, tmpsize, u, used; 822 const int per_fd = 20; 823 824 if ((fds = malloc(bytes)) == NULL) 825 err(1, "Cannot malloc %d bytes for fd_set array", 826 bytes); 827 if (get_struct(pid, (void *)args[sc->offset], fds, bytes) 828 != -1) { 829 used = 0; 830 tmpsize = 1 + numfds * per_fd + 2; 831 if ((tmp = malloc(tmpsize)) == NULL) 832 err(1, "Cannot alloc %d bytes for fd_set " 833 "output", tmpsize); 834 835 tmp[used++] = '{'; 836 for (i = 0; i < numfds; i++) { 837 if (FD_ISSET(i, fds)) { 838 u = snprintf(tmp + used, per_fd, "%d ", 839 i); 840 if (u > 0) 841 used += u < per_fd ? u : per_fd; 842 } 843 } 844 if (tmp[used-1] == ' ') 845 used--; 846 tmp[used++] = '}'; 847 tmp[used++] = '\0'; 848 } else 849 asprintf(&tmp, "0x%lx", args[sc->offset]); 850 free(fds); 851 break; 852 } 853 case Signal: 854 tmp = strsig2(args[sc->offset]); 855 break; 856 case Sigset: { 857 long sig; 858 sigset_t ss; 859 int i, used; 860 char *signame; 861 862 sig = args[sc->offset]; 863 if (get_struct(pid, (void *)args[sc->offset], (void *)&ss, 864 sizeof(ss)) == -1) { 865 asprintf(&tmp, "0x%lx", args[sc->offset]); 866 break; 867 } 868 tmp = malloc(sys_nsig * 8); /* 7 bytes avg per signal name */ 869 used = 0; 870 for (i = 1; i < sys_nsig; i++) { 871 if (sigismember(&ss, i)) { 872 signame = strsig(i); 873 used += sprintf(tmp + used, "%s|", signame); 874 free(signame); 875 } 876 } 877 if (used) 878 tmp[used-1] = 0; 879 else 880 strcpy(tmp, "0x0"); 881 break; 882 } 883 case Sigprocmask: { 884 switch (args[sc->offset]) { 885#define S(a) case a: tmp = strdup(#a); break; 886 S(SIG_BLOCK); 887 S(SIG_UNBLOCK); 888 S(SIG_SETMASK); 889#undef S 890 } 891 if (tmp == NULL) 892 asprintf(&tmp, "0x%lx", args[sc->offset]); 893 break; 894 } 895 case Fcntlflag: { 896 /* XXX output depends on the value of the previous argument */ 897 switch (args[sc->offset-1]) { 898 case F_SETFD: 899 tmp = strdup(xlookup_bits(fcntlfd_arg, 900 args[sc->offset])); 901 break; 902 case F_SETFL: 903 tmp = strdup(xlookup_bits(fcntlfl_arg, 904 args[sc->offset])); 905 break; 906 case F_GETFD: 907 case F_GETFL: 908 case F_GETOWN: 909 tmp = strdup(""); 910 break; 911 default: 912 asprintf(&tmp, "0x%lx", args[sc->offset]); 913 break; 914 } 915 break; 916 } 917 case Open: 918 tmp = strdup(xlookup_bits(open_flags, args[sc->offset])); 919 break; 920 case Fcntl: 921 tmp = strdup(xlookup(fcntl_arg, args[sc->offset])); 922 break; 923 case Mprot: 924 tmp = strdup(xlookup_bits(mprot_flags, args[sc->offset])); 925 break; 926 case Mmapflags: { 927 char *base, *alignstr; 928 int align, flags; 929 930 /* 931 * MAP_ALIGNED can't be handled by xlookup_bits(), so 932 * generate that string manually and prepend it to the 933 * string from xlookup_bits(). Have to be careful to 934 * avoid outputting MAP_ALIGNED|0 if MAP_ALIGNED is 935 * the only flag. 936 */ 937 flags = args[sc->offset] & ~MAP_ALIGNMENT_MASK; 938 align = args[sc->offset] & MAP_ALIGNMENT_MASK; 939 if (align != 0) { 940 if (align == MAP_ALIGNED_SUPER) 941 alignstr = strdup("MAP_ALIGNED_SUPER"); 942 else 943 asprintf(&alignstr, "MAP_ALIGNED(%d)", 944 align >> MAP_ALIGNMENT_SHIFT); 945 if (flags == 0) { 946 tmp = alignstr; 947 break; 948 } 949 } else 950 alignstr = NULL; 951 base = strdup(xlookup_bits(mmap_flags, flags)); 952 if (alignstr == NULL) { 953 tmp = base; 954 break; 955 } 956 asprintf(&tmp, "%s|%s", alignstr, base); 957 free(alignstr); 958 free(base); 959 break; 960 } 961 case Whence: 962 tmp = strdup(xlookup(whence_arg, args[sc->offset])); 963 break; 964 case Sockdomain: 965 tmp = strdup(xlookup(sockdomain_arg, args[sc->offset])); 966 break; 967 case Socktype: 968 tmp = strdup(xlookup(socktype_arg, args[sc->offset])); 969 break; 970 case Shutdown: 971 tmp = strdup(xlookup(shutdown_arg, args[sc->offset])); 972 break; 973 case Resource: 974 tmp = strdup(xlookup(resource_arg, args[sc->offset])); 975 break; 976 case Pathconf: 977 tmp = strdup(xlookup(pathconf_arg, args[sc->offset])); 978 break; 979 case Rforkflags: 980 tmp = strdup(xlookup_bits(rfork_flags, args[sc->offset])); 981 break; 982 case Sockaddr: { 983 struct sockaddr_storage ss; 984 char addr[64]; 985 struct sockaddr_in *lsin; 986 struct sockaddr_in6 *lsin6; 987 struct sockaddr_un *sun; 988 struct sockaddr *sa; 989 char *p; 990 u_char *q; 991 int i; 992 993 if (args[sc->offset] == 0) { 994 asprintf(&tmp, "NULL"); 995 break; 996 } 997 998 /* yuck: get ss_len */ 999 if (get_struct(pid, (void *)args[sc->offset], (void *)&ss, 1000 sizeof(ss.ss_len) + sizeof(ss.ss_family)) == -1) 1001 err(1, "get_struct %p", (void *)args[sc->offset]); 1002 /* 1003 * If ss_len is 0, then try to guess from the sockaddr type. 1004 * AF_UNIX may be initialized incorrectly, so always frob 1005 * it by using the "right" size. 1006 */ 1007 if (ss.ss_len == 0 || ss.ss_family == AF_UNIX) { 1008 switch (ss.ss_family) { 1009 case AF_INET: 1010 ss.ss_len = sizeof(*lsin); 1011 break; 1012 case AF_UNIX: 1013 ss.ss_len = sizeof(*sun); 1014 break; 1015 default: 1016 /* hurrrr */ 1017 break; 1018 } 1019 } 1020 if (get_struct(pid, (void *)args[sc->offset], (void *)&ss, 1021 ss.ss_len) == -1) { 1022 err(2, "get_struct %p", (void *)args[sc->offset]); 1023 } 1024 1025 switch (ss.ss_family) { 1026 case AF_INET: 1027 lsin = (struct sockaddr_in *)&ss; 1028 inet_ntop(AF_INET, &lsin->sin_addr, addr, sizeof addr); 1029 asprintf(&tmp, "{ AF_INET %s:%d }", addr, 1030 htons(lsin->sin_port)); 1031 break; 1032 case AF_INET6: 1033 lsin6 = (struct sockaddr_in6 *)&ss; 1034 inet_ntop(AF_INET6, &lsin6->sin6_addr, addr, 1035 sizeof addr); 1036 asprintf(&tmp, "{ AF_INET6 [%s]:%d }", addr, 1037 htons(lsin6->sin6_port)); 1038 break; 1039 case AF_UNIX: 1040 sun = (struct sockaddr_un *)&ss; 1041 asprintf(&tmp, "{ AF_UNIX \"%s\" }", sun->sun_path); 1042 break; 1043 default: 1044 sa = (struct sockaddr *)&ss; 1045 asprintf(&tmp, "{ sa_len = %d, sa_family = %d, sa_data " 1046 "= {%n%*s } }", (int)sa->sa_len, (int)sa->sa_family, 1047 &i, 6 * (int)(sa->sa_len - ((char *)&sa->sa_data - 1048 (char *)sa)), ""); 1049 if (tmp != NULL) { 1050 p = tmp + i; 1051 for (q = (u_char *)&sa->sa_data; 1052 q < (u_char *)sa + sa->sa_len; q++) 1053 p += sprintf(p, " %#02x,", *q); 1054 } 1055 } 1056 break; 1057 } 1058 case Sigaction: { 1059 struct sigaction sa; 1060 char *hand; 1061 const char *h; 1062 1063 if (get_struct(pid, (void *)args[sc->offset], &sa, sizeof(sa)) 1064 != -1) { 1065 asprintf(&hand, "%p", sa.sa_handler); 1066 if (sa.sa_handler == SIG_DFL) 1067 h = "SIG_DFL"; 1068 else if (sa.sa_handler == SIG_IGN) 1069 h = "SIG_IGN"; 1070 else 1071 h = hand; 1072 1073 asprintf(&tmp, "{ %s %s ss_t }", h, 1074 xlookup_bits(sigaction_flags, sa.sa_flags)); 1075 free(hand); 1076 } else 1077 asprintf(&tmp, "0x%lx", args[sc->offset]); 1078 break; 1079 } 1080 case Kevent: { 1081 /* 1082 * XXX XXX: the size of the array is determined by either the 1083 * next syscall argument, or by the syscall returnvalue, 1084 * depending on which argument number we are. This matches the 1085 * kevent syscall, but luckily that's the only syscall that uses 1086 * them. 1087 */ 1088 struct kevent *ke; 1089 int numevents = -1; 1090 int bytes = 0; 1091 int i, tmpsize, u, used; 1092 const int per_ke = 100; 1093 1094 if (sc->offset == 1) 1095 numevents = args[sc->offset+1]; 1096 else if (sc->offset == 3 && retval != -1) 1097 numevents = retval; 1098 1099 if (numevents >= 0) 1100 bytes = sizeof(struct kevent) * numevents; 1101 if ((ke = malloc(bytes)) == NULL) 1102 err(1, "Cannot malloc %d bytes for kevent array", 1103 bytes); 1104 if (numevents >= 0 && get_struct(pid, (void *)args[sc->offset], 1105 ke, bytes) != -1) { 1106 used = 0; 1107 tmpsize = 1 + per_ke * numevents + 2; 1108 if ((tmp = malloc(tmpsize)) == NULL) 1109 err(1, "Cannot alloc %d bytes for kevent " 1110 "output", tmpsize); 1111 1112 tmp[used++] = '{'; 1113 for (i = 0; i < numevents; i++) { 1114 u = snprintf(tmp + used, per_ke, 1115 "%s%p,%s,%s,%d,%p,%p", 1116 i > 0 ? " " : "", 1117 (void *)ke[i].ident, 1118 xlookup(kevent_filters, ke[i].filter), 1119 xlookup_bits(kevent_flags, ke[i].flags), 1120 ke[i].fflags, 1121 (void *)ke[i].data, 1122 (void *)ke[i].udata); 1123 if (u > 0) 1124 used += u < per_ke ? u : per_ke; 1125 } 1126 tmp[used++] = '}'; 1127 tmp[used++] = '\0'; 1128 } else { 1129 asprintf(&tmp, "0x%lx", args[sc->offset]); 1130 } 1131 free(ke); 1132 break; 1133 } 1134 case Stat: { 1135 struct stat st; 1136 if (get_struct(pid, (void *)args[sc->offset], &st, sizeof(st)) 1137 != -1) { 1138 char mode[12]; 1139 strmode(st.st_mode, mode); 1140 asprintf(&tmp, 1141 "{ mode=%s,inode=%jd,size=%jd,blksize=%ld }", mode, 1142 (intmax_t)st.st_ino, (intmax_t)st.st_size, 1143 (long)st.st_blksize); 1144 } else { 1145 asprintf(&tmp, "0x%lx", args[sc->offset]); 1146 } 1147 break; 1148 } 1149 case Rusage: { 1150 struct rusage ru; 1151 if (get_struct(pid, (void *)args[sc->offset], &ru, sizeof(ru)) 1152 != -1) { 1153 asprintf(&tmp, 1154 "{ u=%ld.%06ld,s=%ld.%06ld,in=%ld,out=%ld }", 1155 (long)ru.ru_utime.tv_sec, ru.ru_utime.tv_usec, 1156 (long)ru.ru_stime.tv_sec, ru.ru_stime.tv_usec, 1157 ru.ru_inblock, ru.ru_oublock); 1158 } else 1159 asprintf(&tmp, "0x%lx", args[sc->offset]); 1160 break; 1161 } 1162 case Rlimit: { 1163 struct rlimit rl; 1164 if (get_struct(pid, (void *)args[sc->offset], &rl, sizeof(rl)) 1165 != -1) { 1166 asprintf(&tmp, "{ cur=%ju,max=%ju }", 1167 rl.rlim_cur, rl.rlim_max); 1168 } else 1169 asprintf(&tmp, "0x%lx", args[sc->offset]); 1170 break; 1171 } 1172 case ExitStatus: { 1173 char *signame; 1174 int status; 1175 signame = NULL; 1176 if (get_struct(pid, (void *)args[sc->offset], &status, 1177 sizeof(status)) != -1) { 1178 if (WIFCONTINUED(status)) 1179 tmp = strdup("{ CONTINUED }"); 1180 else if (WIFEXITED(status)) 1181 asprintf(&tmp, "{ EXITED,val=%d }", 1182 WEXITSTATUS(status)); 1183 else if (WIFSIGNALED(status)) 1184 asprintf(&tmp, "{ SIGNALED,sig=%s%s }", 1185 signame = strsig2(WTERMSIG(status)), 1186 WCOREDUMP(status) ? ",cored" : ""); 1187 else 1188 asprintf(&tmp, "{ STOPPED,sig=%s }", 1189 signame = strsig2(WTERMSIG(status))); 1190 } else 1191 asprintf(&tmp, "0x%lx", args[sc->offset]); 1192 free(signame); 1193 break; 1194 } 1195 case Waitoptions: 1196 tmp = strdup(xlookup_bits(wait_options, args[sc->offset])); 1197 break; 1198 case Idtype: 1199 tmp = strdup(xlookup(idtype_arg, args[sc->offset])); 1200 break; 1201 default: 1202 errx(1, "Invalid argument type %d\n", sc->type & ARG_MASK); 1203 } 1204 return (tmp); 1205} 1206 1207/* 1208 * print_syscall 1209 * Print (to outfile) the system call and its arguments. Note that 1210 * nargs is the number of arguments (not the number of words; this is 1211 * potentially confusing, I know). 1212 */ 1213 1214void 1215print_syscall(struct trussinfo *trussinfo, const char *name, int nargs, 1216 char **s_args) 1217{ 1218 struct timespec timediff; 1219 int i, len; 1220 1221 len = 0; 1222 if (trussinfo->flags & FOLLOWFORKS) 1223 len += fprintf(trussinfo->outfile, "%5d: ", trussinfo->pid); 1224 1225 if (name != NULL && (strcmp(name, "execve") == 0 || 1226 strcmp(name, "exit") == 0)) { 1227 clock_gettime(CLOCK_REALTIME, &trussinfo->curthread->after); 1228 } 1229 1230 if (trussinfo->flags & ABSOLUTETIMESTAMPS) { 1231 timespecsubt(&trussinfo->curthread->after, 1232 &trussinfo->start_time, &timediff); 1233 len += fprintf(trussinfo->outfile, "%ld.%09ld ", 1234 (long)timediff.tv_sec, timediff.tv_nsec); 1235 } 1236 1237 if (trussinfo->flags & RELATIVETIMESTAMPS) { 1238 timespecsubt(&trussinfo->curthread->after, 1239 &trussinfo->curthread->before, &timediff); 1240 len += fprintf(trussinfo->outfile, "%ld.%09ld ", 1241 (long)timediff.tv_sec, timediff.tv_nsec); 1242 } 1243 1244 len += fprintf(trussinfo->outfile, "%s(", name); 1245 1246 for (i = 0; i < nargs; i++) { 1247 if (s_args[i]) 1248 len += fprintf(trussinfo->outfile, "%s", s_args[i]); 1249 else 1250 len += fprintf(trussinfo->outfile, 1251 "<missing argument>"); 1252 len += fprintf(trussinfo->outfile, "%s", i < (nargs - 1) ? 1253 "," : ""); 1254 } 1255 len += fprintf(trussinfo->outfile, ")"); 1256 for (i = 0; i < 6 - (len / 8); i++) 1257 fprintf(trussinfo->outfile, "\t"); 1258} 1259 1260void 1261print_syscall_ret(struct trussinfo *trussinfo, const char *name, int nargs, 1262 char **s_args, int errorp, long retval, struct syscall *sc) 1263{ 1264 struct timespec timediff; 1265 1266 if (trussinfo->flags & COUNTONLY) { 1267 if (!sc) 1268 return; 1269 clock_gettime(CLOCK_REALTIME, &trussinfo->curthread->after); 1270 timespecsubt(&trussinfo->curthread->after, 1271 &trussinfo->curthread->before, &timediff); 1272 timespecadd(&sc->time, &timediff, &sc->time); 1273 sc->ncalls++; 1274 if (errorp) 1275 sc->nerror++; 1276 return; 1277 } 1278 1279 print_syscall(trussinfo, name, nargs, s_args); 1280 fflush(trussinfo->outfile); 1281 if (errorp) 1282 fprintf(trussinfo->outfile, " ERR#%ld '%s'\n", retval, 1283 strerror(retval)); 1284 else { 1285 /* 1286 * Because pipe(2) has a special assembly glue to provide the 1287 * libc API, we have to adjust retval. 1288 */ 1289 if (name != NULL && strcmp(name, "pipe") == 0) 1290 retval = 0; 1291 fprintf(trussinfo->outfile, " = %ld (0x%lx)\n", retval, retval); 1292 } 1293} 1294 1295void 1296print_summary(struct trussinfo *trussinfo) 1297{ 1298 struct timespec total = {0, 0}; 1299 struct syscall *sc; 1300 int ncall, nerror; 1301 1302 fprintf(trussinfo->outfile, "%-20s%15s%8s%8s\n", 1303 "syscall", "seconds", "calls", "errors"); 1304 ncall = nerror = 0; 1305 for (sc = syscalls; sc->name != NULL; sc++) 1306 if (sc->ncalls) { 1307 fprintf(trussinfo->outfile, "%-20s%5jd.%09ld%8d%8d\n", 1308 sc->name, (intmax_t)sc->time.tv_sec, 1309 sc->time.tv_nsec, sc->ncalls, sc->nerror); 1310 timespecadd(&total, &sc->time, &total); 1311 ncall += sc->ncalls; 1312 nerror += sc->nerror; 1313 } 1314 fprintf(trussinfo->outfile, "%20s%15s%8s%8s\n", 1315 "", "-------------", "-------", "-------"); 1316 fprintf(trussinfo->outfile, "%-20s%5jd.%09ld%8d%8d\n", 1317 "", (intmax_t)total.tv_sec, total.tv_nsec, ncall, nerror); 1318} 1319