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