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