131567Ssef/* 2204977Simp * Copyright 1997 Sean Eric Fagan 331899Ssef * 431899Ssef * Redistribution and use in source and binary forms, with or without 531899Ssef * modification, are permitted provided that the following conditions 631899Ssef * are met: 731899Ssef * 1. Redistributions of source code must retain the above copyright 831899Ssef * notice, this list of conditions and the following disclaimer. 931899Ssef * 2. Redistributions in binary form must reproduce the above copyright 1031899Ssef * notice, this list of conditions and the following disclaimer in the 1131899Ssef * documentation and/or other materials provided with the distribution. 1231899Ssef * 3. All advertising materials mentioning features or use of this software 1331899Ssef * must display the following acknowledgement: 1431899Ssef * This product includes software developed by Sean Eric Fagan 1531899Ssef * 4. Neither the name of the author may be used to endorse or promote 1631899Ssef * products derived from this software without specific prior written 1731899Ssef * permission. 1831899Ssef * 1931899Ssef * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 2031899Ssef * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2131899Ssef * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2231899Ssef * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2331899Ssef * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2431899Ssef * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2531899Ssef * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2631899Ssef * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2731899Ssef * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2831899Ssef * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2931899Ssef * SUCH DAMAGE. 3031899Ssef */ 3131899Ssef 3232275Scharnier#ifndef lint 3332275Scharnierstatic const char rcsid[] = 3450477Speter "$FreeBSD$"; 3532275Scharnier#endif /* not lint */ 3632275Scharnier 3731899Ssef/* 3831567Ssef * This file has routines used to print out system calls and their 3931567Ssef * arguments. 4031567Ssef */ 4131567Ssef 42255493Sjhb#include <sys/types.h> 43127328Salfred#include <sys/mman.h> 44255708Sjhb#include <sys/procctl.h> 45168569Sdelphij#include <sys/ptrace.h> 4685292Sdes#include <sys/socket.h> 47104581Smike#include <sys/time.h> 4885292Sdes#include <sys/un.h> 49255493Sjhb#include <sys/wait.h> 5085292Sdes#include <netinet/in.h> 5185292Sdes#include <arpa/inet.h> 52158630Spav#include <sys/ioccom.h> 53158630Spav#include <machine/atomic.h> 54158630Spav#include <errno.h> 55158630Spav#include <sys/umtx.h> 56158630Spav#include <sys/event.h> 57158630Spav#include <sys/stat.h> 58158630Spav#include <sys/resource.h> 5985292Sdes 6086138Sgreen#include <ctype.h> 6132275Scharnier#include <err.h> 62127328Salfred#include <fcntl.h> 63127332Sdwmalone#include <poll.h> 6485292Sdes#include <signal.h> 65127332Sdwmalone#include <stdint.h> 6631567Ssef#include <stdio.h> 6731567Ssef#include <stdlib.h> 6831567Ssef#include <string.h> 69101423Smdodd#include <time.h> 7031567Ssef#include <unistd.h> 71158630Spav#include <vis.h> 7285292Sdes 73101282Smdodd#include "truss.h" 7487703Smarkm#include "extern.h" 7531567Ssef#include "syscall.h" 7631567Ssef 77171646Smarcel/* 64-bit alignment on 32-bit platforms. */ 78171646Smarcel#ifdef __powerpc__ 79171646Smarcel#define QUAD_ALIGN 1 80171646Smarcel#else 81171646Smarcel#define QUAD_ALIGN 0 82171646Smarcel#endif 83171646Smarcel 84171646Smarcel/* Number of slots needed for a 64-bit argument. */ 85171646Smarcel#ifdef __LP64__ 86171646Smarcel#define QUAD_SLOTS 1 87171646Smarcel#else 88171646Smarcel#define QUAD_SLOTS 2 89171646Smarcel#endif 90171646Smarcel 9131567Ssef/* 92158630Spav * This should probably be in its own file, sorted alphabetically. 9331567Ssef */ 94228396Sedstatic struct syscall syscalls[] = { 95192025Sdds { .name = "fcntl", .ret_type = 1, .nargs = 3, 96192025Sdds .args = { { Int, 0 } , { Fcntl, 1 }, { Fcntlflag | OUT, 2 } } }, 97200751Sjh { .name = "fork", .ret_type = 1, .nargs = 0 }, 98253850Smarkj { .name = "vfork", .ret_type = 1, .nargs = 0 }, 99253850Smarkj { .name = "rfork", .ret_type = 1, .nargs = 1, 100253850Smarkj .args = { { Rforkflags, 0 } } }, 101200751Sjh { .name = "getegid", .ret_type = 1, .nargs = 0 }, 102200751Sjh { .name = "geteuid", .ret_type = 1, .nargs = 0 }, 103200751Sjh { .name = "getgid", .ret_type = 1, .nargs = 0 }, 104200751Sjh { .name = "getpid", .ret_type = 1, .nargs = 0 }, 105200751Sjh { .name = "getpgid", .ret_type = 1, .nargs = 1, 106200751Sjh .args = { { Int, 0 } } }, 107200751Sjh { .name = "getpgrp", .ret_type = 1, .nargs = 0 }, 108200751Sjh { .name = "getppid", .ret_type = 1, .nargs = 0 }, 109200751Sjh { .name = "getsid", .ret_type = 1, .nargs = 1, 110200751Sjh .args = { { Int, 0 } } }, 111200751Sjh { .name = "getuid", .ret_type = 1, .nargs = 0 }, 112192025Sdds { .name = "readlink", .ret_type = 1, .nargs = 3, 113192025Sdds .args = { { Name, 0 } , { Readlinkres | OUT, 1 }, { Int, 2 } } }, 114192025Sdds { .name = "lseek", .ret_type = 2, .nargs = 3, 115192025Sdds .args = { { Int, 0 }, { Quad, 1 + QUAD_ALIGN }, { Whence, 1 + QUAD_SLOTS + QUAD_ALIGN } } }, 116192025Sdds { .name = "linux_lseek", .ret_type = 2, .nargs = 3, 117192025Sdds .args = { { Int, 0 }, { Int, 1 }, { Whence, 2 } } }, 118192025Sdds { .name = "mmap", .ret_type = 2, .nargs = 6, 119192025Sdds .args = { { Ptr, 0 }, { Int, 1 }, { Mprot, 2 }, { Mmapflags, 3 }, { Int, 4 }, { Quad, 5 + QUAD_ALIGN } } }, 120192025Sdds { .name = "mprotect", .ret_type = 1, .nargs = 3, 121192025Sdds .args = { { Ptr, 0 }, { Int, 1 }, { Mprot, 2 } } }, 122192025Sdds { .name = "open", .ret_type = 1, .nargs = 3, 123192025Sdds .args = { { Name | IN, 0 } , { Open, 1 }, { Octal, 2 } } }, 124192025Sdds { .name = "mkdir", .ret_type = 1, .nargs = 2, 125192025Sdds .args = { { Name, 0 } , { Octal, 1 } } }, 126192025Sdds { .name = "linux_open", .ret_type = 1, .nargs = 3, 127192025Sdds .args = { { Name, 0 }, { Hex, 1 }, { Octal, 2 } } }, 128192025Sdds { .name = "close", .ret_type = 1, .nargs = 1, 129192025Sdds .args = { { Int, 0 } } }, 130192025Sdds { .name = "link", .ret_type = 0, .nargs = 2, 131192025Sdds .args = { { Name, 0 }, { Name, 1 } } }, 132192025Sdds { .name = "unlink", .ret_type = 0, .nargs = 1, 133192025Sdds .args = { { Name, 0 } } }, 134192025Sdds { .name = "chdir", .ret_type = 0, .nargs = 1, 135192025Sdds .args = { { Name, 0 } } }, 136192025Sdds { .name = "chroot", .ret_type = 0, .nargs = 1, 137192025Sdds .args = { { Name, 0 } } }, 138192025Sdds { .name = "mknod", .ret_type = 0, .nargs = 3, 139192025Sdds .args = { { Name, 0 }, { Octal, 1 }, { Int, 3 } } }, 140192025Sdds { .name = "chmod", .ret_type = 0, .nargs = 2, 141192025Sdds .args = { { Name, 0 }, { Octal, 1 } } }, 142192025Sdds { .name = "chown", .ret_type = 0, .nargs = 3, 143192025Sdds .args = { { Name, 0 }, { Int, 1 }, { Int, 2 } } }, 144192025Sdds { .name = "mount", .ret_type = 0, .nargs = 4, 145192025Sdds .args = { { Name, 0 }, { Name, 1 }, { Int, 2 }, { Ptr, 3 } } }, 146192025Sdds { .name = "umount", .ret_type = 0, .nargs = 2, 147192025Sdds .args = { { Name, 0 }, { Int, 2 } } }, 148192025Sdds { .name = "fstat", .ret_type = 1, .nargs = 2, 149192025Sdds .args = { { Int, 0 }, { Stat | OUT , 1 } } }, 150192025Sdds { .name = "stat", .ret_type = 1, .nargs = 2, 151192025Sdds .args = { { Name | IN, 0 }, { Stat | OUT, 1 } } }, 152192025Sdds { .name = "lstat", .ret_type = 1, .nargs = 2, 153192025Sdds .args = { { Name | IN, 0 }, { Stat | OUT, 1 } } }, 154192025Sdds { .name = "linux_newstat", .ret_type = 1, .nargs = 2, 155192025Sdds .args = { { Name | IN, 0 }, { Ptr | OUT, 1 } } }, 156192025Sdds { .name = "linux_newfstat", .ret_type = 1, .nargs = 2, 157192025Sdds .args = { { Int, 0 }, { Ptr | OUT, 1 } } }, 158192025Sdds { .name = "write", .ret_type = 1, .nargs = 3, 159192025Sdds .args = { { Int, 0 }, { BinString | IN, 1 }, { Int, 2 } } }, 160192025Sdds { .name = "ioctl", .ret_type = 1, .nargs = 3, 161192025Sdds .args = { { Int, 0 }, { Ioctl, 1 }, { Hex, 2 } } }, 162192025Sdds { .name = "break", .ret_type = 1, .nargs = 1, 163192025Sdds .args = { { Ptr, 0 } } }, 164192025Sdds { .name = "exit", .ret_type = 0, .nargs = 1, 165192025Sdds .args = { { Hex, 0 } } }, 166192025Sdds { .name = "access", .ret_type = 1, .nargs = 2, 167192025Sdds .args = { { Name | IN, 0 }, { Int, 1 } } }, 168192025Sdds { .name = "sigaction", .ret_type = 1, .nargs = 3, 169192025Sdds .args = { { Signal, 0 }, { Sigaction | IN, 1 }, { Sigaction | OUT, 2 } } }, 170192025Sdds { .name = "accept", .ret_type = 1, .nargs = 3, 171192025Sdds .args = { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } }, 172192025Sdds { .name = "bind", .ret_type = 1, .nargs = 3, 173192025Sdds .args = { { Int, 0 }, { Sockaddr | IN, 1 }, { Int, 2 } } }, 174192025Sdds { .name = "connect", .ret_type = 1, .nargs = 3, 175192025Sdds .args = { { Int, 0 }, { Sockaddr | IN, 1 }, { Int, 2 } } }, 176192025Sdds { .name = "getpeername", .ret_type = 1, .nargs = 3, 177192025Sdds .args = { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } }, 178192025Sdds { .name = "getsockname", .ret_type = 1, .nargs = 3, 179192025Sdds .args = { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } }, 180192025Sdds { .name = "recvfrom", .ret_type = 1, .nargs = 6, 181192025Sdds .args = { { Int, 0 }, { BinString | OUT, 1 }, { Int, 2 }, { Hex, 3 }, { Sockaddr | OUT, 4 }, { Ptr | OUT, 5 } } }, 182192025Sdds { .name = "sendto", .ret_type = 1, .nargs = 6, 183192025Sdds .args = { { Int, 0 }, { BinString | IN, 1 }, { Int, 2 }, { Hex, 3 }, { Sockaddr | IN, 4 }, { Ptr | IN, 5 } } }, 184192025Sdds { .name = "execve", .ret_type = 1, .nargs = 3, 185192025Sdds .args = { { Name | IN, 0 }, { StringArray | IN, 1 }, { StringArray | IN, 2 } } }, 186192025Sdds { .name = "linux_execve", .ret_type = 1, .nargs = 3, 187192025Sdds .args = { { Name | IN, 0 }, { StringArray | IN, 1 }, { StringArray | IN, 2 } } }, 188192025Sdds { .name = "kldload", .ret_type = 0, .nargs = 1, 189192025Sdds .args = { { Name | IN, 0 } } }, 190192025Sdds { .name = "kldunload", .ret_type = 0, .nargs = 1, 191192025Sdds .args = { { Int, 0 } } }, 192192025Sdds { .name = "kldfind", .ret_type = 0, .nargs = 1, 193192025Sdds .args = { { Name | IN, 0 } } }, 194192025Sdds { .name = "kldnext", .ret_type = 0, .nargs = 1, 195192025Sdds .args = { { Int, 0 } } }, 196192025Sdds { .name = "kldstat", .ret_type = 0, .nargs = 2, 197192025Sdds .args = { { Int, 0 }, { Ptr, 1 } } }, 198192025Sdds { .name = "kldfirstmod", .ret_type = 0, .nargs = 1, 199192025Sdds .args = { { Int, 0 } } }, 200192025Sdds { .name = "nanosleep", .ret_type = 0, .nargs = 1, 201192025Sdds .args = { { Timespec, 0 } } }, 202192025Sdds { .name = "select", .ret_type = 1, .nargs = 5, 203192025Sdds .args = { { Int, 0 }, { Fd_set, 1 }, { Fd_set, 2 }, { Fd_set, 3 }, { Timeval, 4 } } }, 204192025Sdds { .name = "poll", .ret_type = 1, .nargs = 3, 205192025Sdds .args = { { Pollfd, 0 }, { Int, 1 }, { Int, 2 } } }, 206192025Sdds { .name = "gettimeofday", .ret_type = 1, .nargs = 2, 207192025Sdds .args = { { Timeval | OUT, 0 }, { Ptr, 1 } } }, 208192025Sdds { .name = "clock_gettime", .ret_type = 1, .nargs = 2, 209192025Sdds .args = { { Int, 0 }, { Timespec | OUT, 1 } } }, 210192025Sdds { .name = "getitimer", .ret_type = 1, .nargs = 2, 211192025Sdds .args = { { Int, 0 }, { Itimerval | OUT, 2 } } }, 212192025Sdds { .name = "setitimer", .ret_type = 1, .nargs = 3, 213192025Sdds .args = { { Int, 0 }, { Itimerval, 1 } , { Itimerval | OUT, 2 } } }, 214192025Sdds { .name = "kse_release", .ret_type = 0, .nargs = 1, 215192025Sdds .args = { { Timespec, 0 } } }, 216192025Sdds { .name = "kevent", .ret_type = 0, .nargs = 6, 217192025Sdds .args = { { Int, 0 }, { Kevent, 1 }, { Int, 2 }, { Kevent | OUT, 3 }, { Int, 4 }, { Timespec, 5 } } }, 218192025Sdds { .name = "_umtx_lock", .ret_type = 0, .nargs = 1, 219192025Sdds .args = { { Umtx, 0 } } }, 220192025Sdds { .name = "_umtx_unlock", .ret_type = 0, .nargs = 1, 221192025Sdds .args = { { Umtx, 0 } } }, 222192025Sdds { .name = "sigprocmask", .ret_type = 0, .nargs = 3, 223192025Sdds .args = { { Sigprocmask, 0 }, { Sigset, 1 }, { Sigset | OUT, 2 } } }, 224192025Sdds { .name = "unmount", .ret_type = 1, .nargs = 2, 225192025Sdds .args = { { Name, 0 }, { Int, 1 } } }, 226192025Sdds { .name = "socket", .ret_type = 1, .nargs = 3, 227192025Sdds .args = { { Sockdomain, 0 }, { Socktype, 1 }, { Int, 2 } } }, 228192025Sdds { .name = "getrusage", .ret_type = 1, .nargs = 2, 229192025Sdds .args = { { Int, 0 }, { Rusage | OUT, 1 } } }, 230192025Sdds { .name = "__getcwd", .ret_type = 1, .nargs = 2, 231192025Sdds .args = { { Name | OUT, 0 }, { Int, 1 } } }, 232192025Sdds { .name = "shutdown", .ret_type = 1, .nargs = 2, 233192025Sdds .args = { { Int, 0 }, { Shutdown, 1 } } }, 234192025Sdds { .name = "getrlimit", .ret_type = 1, .nargs = 2, 235192025Sdds .args = { { Resource, 0 }, { Rlimit | OUT, 1 } } }, 236192025Sdds { .name = "setrlimit", .ret_type = 1, .nargs = 2, 237192025Sdds .args = { { Resource, 0 }, { Rlimit | IN, 1 } } }, 238192025Sdds { .name = "utimes", .ret_type = 1, .nargs = 2, 239192025Sdds .args = { { Name | IN, 0 }, { Timeval2 | IN, 1 } } }, 240192025Sdds { .name = "lutimes", .ret_type = 1, .nargs = 2, 241192025Sdds .args = { { Name | IN, 0 }, { Timeval2 | IN, 1 } } }, 242192025Sdds { .name = "futimes", .ret_type = 1, .nargs = 2, 243192025Sdds .args = { { Int, 0 }, { Timeval | IN, 1 } } }, 244192025Sdds { .name = "chflags", .ret_type = 1, .nargs = 2, 245192025Sdds .args = { { Name | IN, 0 }, { Hex, 1 } } }, 246192025Sdds { .name = "lchflags", .ret_type = 1, .nargs = 2, 247192025Sdds .args = { { Name | IN, 0 }, { Hex, 1 } } }, 248192025Sdds { .name = "pathconf", .ret_type = 1, .nargs = 2, 249192025Sdds .args = { { Name | IN, 0 }, { Pathconf, 1 } } }, 250200780Sjh { .name = "pipe", .ret_type = 1, .nargs = 1, 251200780Sjh .args = { { Ptr, 0 } } }, 252192025Sdds { .name = "truncate", .ret_type = 1, .nargs = 3, 253192025Sdds .args = { { Name | IN, 0 }, { Int | IN, 1 }, { Quad | IN, 2 } } }, 254192025Sdds { .name = "ftruncate", .ret_type = 1, .nargs = 3, 255192025Sdds .args = { { Int | IN, 0 }, { Int | IN, 1 }, { Quad | IN, 2 } } }, 256192025Sdds { .name = "kill", .ret_type = 1, .nargs = 2, 257192025Sdds .args = { { Int | IN, 0 }, { Signal | IN, 1 } } }, 258192025Sdds { .name = "munmap", .ret_type = 1, .nargs = 2, 259192025Sdds .args = { { Ptr, 0 }, { Int, 1 } } }, 260192025Sdds { .name = "read", .ret_type = 1, .nargs = 3, 261192025Sdds .args = { { Int, 0 }, { BinString | OUT, 1 }, { Int, 2 } } }, 262192025Sdds { .name = "rename", .ret_type = 1, .nargs = 2, 263192025Sdds .args = { { Name , 0 } , { Name, 1 } } }, 264192025Sdds { .name = "symlink", .ret_type = 1, .nargs = 2, 265192025Sdds .args = { { Name , 0 } , { Name, 1 } } }, 266200902Sed { .name = "posix_openpt", .ret_type = 1, .nargs = 1, 267200902Sed .args = { { Open, 0 } } }, 268255493Sjhb { .name = "wait4", .ret_type = 1, .nargs = 4, 269255493Sjhb .args = { { Int, 0 }, { ExitStatus | OUT, 1 }, { Waitoptions, 2 }, 270255493Sjhb { Rusage | OUT, 3 } } }, 271255493Sjhb { .name = "wait6", .ret_type = 1, .nargs = 6, 272255493Sjhb .args = { { Idtype, 0 }, { Int, 1 }, { ExitStatus | OUT, 2 }, 273255493Sjhb { Waitoptions, 3 }, { Rusage | OUT, 4 }, { Ptr, 5 } } }, 274255708Sjhb { .name = "procctl", .ret_type = 1, .nargs = 4, 275255708Sjhb .args = { { Idtype, 0 }, { Int, 1 }, { Procctl, 2 }, { Ptr, 3 } } }, 276192025Sdds { .name = 0 }, 27731567Ssef}; 27831567Ssef 279158630Spav/* Xlat idea taken from strace */ 280158630Spavstruct xlat { 281158630Spav int val; 282168569Sdelphij const char *str; 283158630Spav}; 284158630Spav 285240005Szont#define X(a) { a, #a }, 286240005Szont#define XEND { 0, NULL } 287158630Spav 288158630Spavstatic struct xlat kevent_filters[] = { 289158630Spav X(EVFILT_READ) X(EVFILT_WRITE) X(EVFILT_AIO) X(EVFILT_VNODE) 290158630Spav X(EVFILT_PROC) X(EVFILT_SIGNAL) X(EVFILT_TIMER) 291201350Sbrooks X(EVFILT_FS) X(EVFILT_READ) XEND 292158630Spav}; 293158630Spav 294158630Spavstatic struct xlat kevent_flags[] = { 295158630Spav X(EV_ADD) X(EV_DELETE) X(EV_ENABLE) X(EV_DISABLE) X(EV_ONESHOT) 296158630Spav X(EV_CLEAR) X(EV_FLAG1) X(EV_ERROR) X(EV_EOF) XEND 297158630Spav}; 298158630Spav 299228396Sedstatic struct xlat poll_flags[] = { 300158630Spav X(POLLSTANDARD) X(POLLIN) X(POLLPRI) X(POLLOUT) X(POLLERR) 301158630Spav X(POLLHUP) X(POLLNVAL) X(POLLRDNORM) X(POLLRDBAND) 302158630Spav X(POLLWRBAND) X(POLLINIGNEOF) XEND 303158630Spav}; 304158630Spav 305158630Spavstatic struct xlat mmap_flags[] = { 306158630Spav X(MAP_SHARED) X(MAP_PRIVATE) X(MAP_FIXED) X(MAP_RENAME) 307158630Spav X(MAP_NORESERVE) X(MAP_RESERVED0080) X(MAP_RESERVED0100) 308158630Spav X(MAP_HASSEMAPHORE) X(MAP_STACK) X(MAP_NOSYNC) X(MAP_ANON) 309255426Sjhb X(MAP_NOCORE) X(MAP_PREFAULT_READ) 310255426Sjhb#ifdef MAP_32BIT 311255426Sjhb X(MAP_32BIT) 312255426Sjhb#endif 313255426Sjhb XEND 314158630Spav}; 315158630Spav 316158630Spavstatic struct xlat mprot_flags[] = { 317158630Spav X(PROT_NONE) X(PROT_READ) X(PROT_WRITE) X(PROT_EXEC) XEND 318158630Spav}; 319158630Spav 320158630Spavstatic struct xlat whence_arg[] = { 321158630Spav X(SEEK_SET) X(SEEK_CUR) X(SEEK_END) XEND 322158630Spav}; 323158630Spav 324158630Spavstatic struct xlat sigaction_flags[] = { 325158630Spav X(SA_ONSTACK) X(SA_RESTART) X(SA_RESETHAND) X(SA_NOCLDSTOP) 326158630Spav X(SA_NODEFER) X(SA_NOCLDWAIT) X(SA_SIGINFO) XEND 327158630Spav}; 328158630Spav 329158630Spavstatic struct xlat fcntl_arg[] = { 330158630Spav X(F_DUPFD) X(F_GETFD) X(F_SETFD) X(F_GETFL) X(F_SETFL) 331158630Spav X(F_GETOWN) X(F_SETOWN) X(F_GETLK) X(F_SETLK) X(F_SETLKW) XEND 332158630Spav}; 333158630Spav 334158630Spavstatic struct xlat fcntlfd_arg[] = { 335158630Spav X(FD_CLOEXEC) XEND 336158630Spav}; 337158630Spav 338158630Spavstatic struct xlat fcntlfl_arg[] = { 339158630Spav X(O_APPEND) X(O_ASYNC) X(O_FSYNC) X(O_NONBLOCK) X(O_NOFOLLOW) 340158630Spav X(O_DIRECT) XEND 341158630Spav}; 342158630Spav 343158630Spavstatic struct xlat sockdomain_arg[] = { 344158630Spav X(PF_UNSPEC) X(PF_LOCAL) X(PF_UNIX) X(PF_INET) X(PF_IMPLINK) 345158630Spav X(PF_PUP) X(PF_CHAOS) X(PF_NETBIOS) X(PF_ISO) X(PF_OSI) 346158630Spav X(PF_ECMA) X(PF_DATAKIT) X(PF_CCITT) X(PF_SNA) X(PF_DECnet) 347158630Spav X(PF_DLI) X(PF_LAT) X(PF_HYLINK) X(PF_APPLETALK) X(PF_ROUTE) 348158630Spav X(PF_LINK) X(PF_XTP) X(PF_COIP) X(PF_CNT) X(PF_SIP) X(PF_IPX) 349158630Spav X(PF_RTIP) X(PF_PIP) X(PF_ISDN) X(PF_KEY) X(PF_INET6) 350158630Spav X(PF_NATM) X(PF_ATM) X(PF_NETGRAPH) X(PF_SLOW) X(PF_SCLUSTER) 351158630Spav X(PF_ARP) X(PF_BLUETOOTH) XEND 352158630Spav}; 353158630Spav 354158630Spavstatic struct xlat socktype_arg[] = { 355158630Spav X(SOCK_STREAM) X(SOCK_DGRAM) X(SOCK_RAW) X(SOCK_RDM) 356158630Spav X(SOCK_SEQPACKET) XEND 357158630Spav}; 358158630Spav 359158630Spavstatic struct xlat open_flags[] = { 360158630Spav X(O_RDONLY) X(O_WRONLY) X(O_RDWR) X(O_ACCMODE) X(O_NONBLOCK) 361158630Spav X(O_APPEND) X(O_SHLOCK) X(O_EXLOCK) X(O_ASYNC) X(O_FSYNC) 362158630Spav X(O_NOFOLLOW) X(O_CREAT) X(O_TRUNC) X(O_EXCL) X(O_NOCTTY) 363252414Smjg X(O_DIRECT) X(O_DIRECTORY) X(O_EXEC) X(O_TTY_INIT) X(O_CLOEXEC) XEND 364158630Spav}; 365158630Spav 366158630Spavstatic struct xlat shutdown_arg[] = { 367158630Spav X(SHUT_RD) X(SHUT_WR) X(SHUT_RDWR) XEND 368158630Spav}; 369158630Spav 370158630Spavstatic struct xlat resource_arg[] = { 371158630Spav X(RLIMIT_CPU) X(RLIMIT_FSIZE) X(RLIMIT_DATA) X(RLIMIT_STACK) 372158630Spav X(RLIMIT_CORE) X(RLIMIT_RSS) X(RLIMIT_MEMLOCK) X(RLIMIT_NPROC) 373158630Spav X(RLIMIT_NOFILE) X(RLIMIT_SBSIZE) X(RLIMIT_VMEM) XEND 374158630Spav}; 375158630Spav 376158630Spavstatic struct xlat pathconf_arg[] = { 377158630Spav X(_PC_LINK_MAX) X(_PC_MAX_CANON) X(_PC_MAX_INPUT) 378158630Spav X(_PC_NAME_MAX) X(_PC_PATH_MAX) X(_PC_PIPE_BUF) 379158630Spav X(_PC_CHOWN_RESTRICTED) X(_PC_NO_TRUNC) X(_PC_VDISABLE) 380158630Spav X(_PC_ASYNC_IO) X(_PC_PRIO_IO) X(_PC_SYNC_IO) 381158630Spav X(_PC_ALLOC_SIZE_MIN) X(_PC_FILESIZEBITS) 382158630Spav X(_PC_REC_INCR_XFER_SIZE) X(_PC_REC_MAX_XFER_SIZE) 383158630Spav X(_PC_REC_MIN_XFER_SIZE) X(_PC_REC_XFER_ALIGN) 384158630Spav X(_PC_SYMLINK_MAX) X(_PC_ACL_EXTENDED) X(_PC_ACL_PATH_MAX) 385158630Spav X(_PC_CAP_PRESENT) X(_PC_INF_PRESENT) X(_PC_MAC_PRESENT) 386158630Spav XEND 387158630Spav}; 388158630Spav 389253850Smarkjstatic struct xlat rfork_flags[] = { 390253850Smarkj X(RFPROC) X(RFNOWAIT) X(RFFDG) X(RFCFDG) X(RFTHREAD) X(RFMEM) 391253850Smarkj X(RFSIGSHARE) X(RFTSIGZMB) X(RFLINUXTHPN) XEND 392253850Smarkj}; 393253850Smarkj 394255493Sjhbstatic struct xlat wait_options[] = { 395255493Sjhb X(WNOHANG) X(WUNTRACED) X(WCONTINUED) X(WNOWAIT) X(WEXITED) 396255493Sjhb X(WTRAPPED) XEND 397255493Sjhb}; 398255493Sjhb 399255493Sjhbstatic struct xlat idtype_arg[] = { 400255493Sjhb X(P_PID) X(P_PPID) X(P_PGID) X(P_SID) X(P_CID) X(P_UID) X(P_GID) 401255493Sjhb X(P_ALL) X(P_LWPID) X(P_TASKID) X(P_PROJID) X(P_POOLID) X(P_JAILID) 402255493Sjhb X(P_CTID) X(P_CPUID) X(P_PSETID) XEND 403255493Sjhb}; 404255493Sjhb 405255708Sjhbstatic struct xlat procctl_arg[] = { 406255708Sjhb X(PROC_SPROTECT) XEND 407255708Sjhb}; 408255708Sjhb 409158630Spav#undef X 410158630Spav#undef XEND 411158630Spav 412181061Sdes/* 413181061Sdes * Searches an xlat array for a value, and returns it if found. Otherwise 414181061Sdes * return a string representation. 415181061Sdes */ 416181061Sdesstatic const char * 417181061Sdeslookup(struct xlat *xlat, int val, int base) 418158630Spav{ 419158630Spav static char tmp[16]; 420181061Sdes 421158630Spav for (; xlat->str != NULL; xlat++) 422158630Spav if (xlat->val == val) 423181061Sdes return (xlat->str); 424158630Spav switch (base) { 425158630Spav case 8: 426158630Spav sprintf(tmp, "0%o", val); 427158630Spav break; 428158630Spav case 16: 429158630Spav sprintf(tmp, "0x%x", val); 430158630Spav break; 431158630Spav case 10: 432158630Spav sprintf(tmp, "%u", val); 433158630Spav break; 434158630Spav default: 435158630Spav errx(1,"Unknown lookup base"); 436158630Spav break; 437158630Spav } 438181061Sdes return (tmp); 439158630Spav} 440158630Spav 441168569Sdelphijstatic const char * 442168569Sdelphijxlookup(struct xlat *xlat, int val) 443158630Spav{ 444181061Sdes 445181061Sdes return (lookup(xlat, val, 16)); 446158630Spav} 447158630Spav 448158630Spav/* Searches an xlat array containing bitfield values. Remaining bits 449158630Spav set after removing the known ones are printed at the end: 450158630Spav IN|0x400 */ 451181061Sdesstatic char * 452181061Sdesxlookup_bits(struct xlat *xlat, int val) 453158630Spav{ 454240005Szont int len, rem; 455158630Spav static char str[512]; 456158630Spav 457240005Szont len = 0; 458240005Szont rem = val; 459181061Sdes for (; xlat->str != NULL; xlat++) { 460181061Sdes if ((xlat->val & rem) == xlat->val) { 461158630Spav /* don't print the "all-bits-zero" string unless all 462158630Spav bits are really zero */ 463158630Spav if (xlat->val == 0 && val != 0) 464158630Spav continue; 465158630Spav len += sprintf(str + len, "%s|", xlat->str); 466158630Spav rem &= ~(xlat->val); 467158630Spav } 468158630Spav } 469158630Spav /* if we have leftover bits or didn't match anything */ 470158630Spav if (rem || len == 0) 471158630Spav len += sprintf(str + len, "0x%x", rem); 472158630Spav if (len && str[len - 1] == '|') 473158630Spav len--; 474158630Spav str[len] = 0; 475181061Sdes return (str); 476158630Spav} 477158630Spav 47831567Ssef/* 47931567Ssef * If/when the list gets big, it might be desirable to do it 48031567Ssef * as a hash table or binary search. 48131567Ssef */ 48231567Ssef 48331567Ssefstruct syscall * 484181061Sdesget_syscall(const char *name) 485181061Sdes{ 486240005Szont struct syscall *sc; 48731567Ssef 488240005Szont sc = syscalls; 489133349Salfred if (name == NULL) 490133349Salfred return (NULL); 49131567Ssef while (sc->name) { 492240005Szont if (strcmp(name, sc->name) == 0) 493181061Sdes return (sc); 49431567Ssef sc++; 49531567Ssef } 496181061Sdes return (NULL); 49731567Ssef} 49831567Ssef 49931567Ssef/* 50085292Sdes * get_struct 50185292Sdes * 50285292Sdes * Copy a fixed amount of bytes from the process. 50385292Sdes */ 50485292Sdes 50587703Smarkmstatic int 506239501Szontget_struct(pid_t pid, void *offset, void *buf, int len) 507181061Sdes{ 508168569Sdelphij struct ptrace_io_desc iorequest; 509181061Sdes 510168569Sdelphij iorequest.piod_op = PIOD_READ_D; 511168569Sdelphij iorequest.piod_offs = offset; 512168569Sdelphij iorequest.piod_addr = buf; 513168569Sdelphij iorequest.piod_len = len; 514168569Sdelphij if (ptrace(PT_IO, pid, (caddr_t)&iorequest, 0) < 0) 515181061Sdes return (-1); 516181061Sdes return (0); 51785292Sdes} 51885292Sdes 519240005Szont#define MAXSIZE 4096 520240005Szont#define BLOCKSIZE 1024 52185292Sdes/* 52231567Ssef * get_string 52331567Ssef * Copy a string from the process. Note that it is 52431567Ssef * expected to be a C string, but if max is set, it will 52531567Ssef * only get that much. 52631567Ssef */ 52731567Ssef 528168569Sdelphijstatic char * 529181061Sdesget_string(pid_t pid, void *offset, int max) 530181061Sdes{ 531240005Szont struct ptrace_io_desc iorequest; 53232275Scharnier char *buf; 533240005Szont int diff, i, size, totalsize; 534181061Sdes 535240005Szont diff = 0; 536181061Sdes totalsize = size = max ? (max + 1) : BLOCKSIZE; 537168569Sdelphij buf = malloc(totalsize); 538168569Sdelphij if (buf == NULL) 539181061Sdes return (NULL); 540181061Sdes for (;;) { 541168569Sdelphij diff = totalsize - size; 542168569Sdelphij iorequest.piod_op = PIOD_READ_D; 543168569Sdelphij iorequest.piod_offs = (char *)offset + diff; 544168569Sdelphij iorequest.piod_addr = buf + diff; 545168569Sdelphij iorequest.piod_len = size; 546168569Sdelphij if (ptrace(PT_IO, pid, (caddr_t)&iorequest, 0) < 0) { 547168569Sdelphij free(buf); 548181061Sdes return (NULL); 54931567Ssef } 550168569Sdelphij for (i = 0 ; i < size; i++) { 551168569Sdelphij if (buf[diff + i] == '\0') 552168569Sdelphij return (buf); 553168569Sdelphij } 554168569Sdelphij if (totalsize < MAXSIZE - BLOCKSIZE && max == 0) { 555168569Sdelphij totalsize += BLOCKSIZE; 556168569Sdelphij buf = realloc(buf, totalsize); 557168569Sdelphij size = BLOCKSIZE; 558181061Sdes } else { 559216224Sjh buf[totalsize - 1] = '\0'; 560181061Sdes return (buf); 561168569Sdelphij } 56231567Ssef } 56331567Ssef} 56431567Ssef 565255493Sjhbstatic char * 566255493Sjhbstrsig2(int sig) 567255493Sjhb{ 568255493Sjhb char *tmp; 56931567Ssef 570255493Sjhb tmp = strsig(sig); 571255493Sjhb if (tmp == NULL) 572255493Sjhb asprintf(&tmp, "%d", sig); 573255493Sjhb return (tmp); 574255493Sjhb} 575255493Sjhb 57631567Ssef/* 57731567Ssef * print_arg 57831567Ssef * Converts a syscall argument into a string. Said string is 57931567Ssef * allocated via malloc(), so needs to be free()'d. The file 58031567Ssef * descriptor is for the process' memory (via /proc), and is used 58131567Ssef * to get any data (where the argument is a pointer). sc is 58231567Ssef * a pointer to the syscall description (see above); args is 58331567Ssef * an array of all of the system call arguments. 58431567Ssef */ 58531567Ssef 58631567Ssefchar * 587240005Szontprint_arg(struct syscall_args *sc, unsigned long *args, long retval, 588240005Szont struct trussinfo *trussinfo) 589181061Sdes{ 590240005Szont char *tmp; 591240005Szont pid_t pid; 592158630Spav 593240005Szont tmp = NULL; 594240005Szont pid = trussinfo->pid; 595181061Sdes switch (sc->type & ARG_MASK) { 596181061Sdes case Hex: 597181061Sdes asprintf(&tmp, "0x%x", (int)args[sc->offset]); 598181061Sdes break; 599181061Sdes case Octal: 600181061Sdes asprintf(&tmp, "0%o", (int)args[sc->offset]); 601181061Sdes break; 602181061Sdes case Int: 603181061Sdes asprintf(&tmp, "%d", (int)args[sc->offset]); 604181061Sdes break; 605181061Sdes case Name: { 606181061Sdes /* NULL-terminated string. */ 607181061Sdes char *tmp2; 608181061Sdes tmp2 = get_string(pid, (void*)args[sc->offset], 0); 609181061Sdes asprintf(&tmp, "\"%s\"", tmp2); 610181061Sdes free(tmp2); 611181061Sdes break; 612181061Sdes } 613181061Sdes case BinString: { 614181061Sdes /* Binary block of data that might have printable characters. 615181061Sdes XXX If type|OUT, assume that the length is the syscall's 616181061Sdes return value. Otherwise, assume that the length of the block 617181061Sdes is in the next syscall argument. */ 618181061Sdes int max_string = trussinfo->strsize; 619181061Sdes char tmp2[max_string+1], *tmp3; 620181061Sdes int len; 621181061Sdes int truncated = 0; 622158630Spav 623181061Sdes if (sc->type & OUT) 624181061Sdes len = retval; 625181061Sdes else 626181061Sdes len = args[sc->offset + 1]; 627101289Smdodd 628181061Sdes /* Don't print more than max_string characters, to avoid word 629181061Sdes wrap. If we have to truncate put some ... after the string. 630181061Sdes */ 631181061Sdes if (len > max_string) { 632181061Sdes len = max_string; 633181061Sdes truncated = 1; 634181061Sdes } 635240005Szont if (len && get_struct(pid, (void*)args[sc->offset], &tmp2, len) 636240005Szont != -1) { 637181061Sdes tmp3 = malloc(len * 4 + 1); 638181061Sdes while (len) { 639240005Szont if (strvisx(tmp3, tmp2, len, 640240005Szont VIS_CSTYLE|VIS_TAB|VIS_NL) <= max_string) 641181061Sdes break; 642181061Sdes len--; 643181061Sdes truncated = 1; 644181061Sdes }; 645240005Szont asprintf(&tmp, "\"%s\"%s", tmp3, truncated ? 646240005Szont "..." : ""); 647181061Sdes free(tmp3); 648181061Sdes } else { 649181061Sdes asprintf(&tmp, "0x%lx", args[sc->offset]); 650181061Sdes } 651181061Sdes break; 652181061Sdes } 653181061Sdes case StringArray: { 654181061Sdes int num, size, i; 655181061Sdes char *tmp2; 656181061Sdes char *string; 657181061Sdes char *strarray[100]; /* XXX This is ugly. */ 658101289Smdodd 659240005Szont if (get_struct(pid, (void *)args[sc->offset], 660240005Szont (void *)&strarray, sizeof(strarray)) == -1) 661181061Sdes err(1, "get_struct %p", (void *)args[sc->offset]); 662181061Sdes num = 0; 663181061Sdes size = 0; 664101289Smdodd 665181061Sdes /* Find out how large of a buffer we'll need. */ 666181061Sdes while (strarray[num] != NULL) { 667181061Sdes string = get_string(pid, (void*)strarray[num], 0); 668181061Sdes size += strlen(string); 669181061Sdes free(string); 670181061Sdes num++; 671181061Sdes } 672181061Sdes size += 4 + (num * 4); 673181061Sdes tmp = (char *)malloc(size); 674181061Sdes tmp2 = tmp; 675181061Sdes 676181061Sdes tmp2 += sprintf(tmp2, " ["); 677181061Sdes for (i = 0; i < num; i++) { 678181061Sdes string = get_string(pid, (void*)strarray[i], 0); 679240005Szont tmp2 += sprintf(tmp2, " \"%s\"%c", string, 680240005Szont (i + 1 == num) ? ' ' : ','); 681181061Sdes free(string); 682181061Sdes } 683181061Sdes tmp2 += sprintf(tmp2, "]"); 684181061Sdes break; 685181061Sdes } 686134799Smarcel#ifdef __LP64__ 687181061Sdes case Quad: 688181061Sdes asprintf(&tmp, "0x%lx", args[sc->offset]); 689181061Sdes break; 690134799Smarcel#else 691181061Sdes case Quad: { 692181061Sdes unsigned long long ll; 693181061Sdes ll = *(unsigned long long *)(args + sc->offset); 694181061Sdes asprintf(&tmp, "0x%llx", ll); 695181061Sdes break; 696181061Sdes } 697134799Smarcel#endif 698181061Sdes case Ptr: 699181061Sdes asprintf(&tmp, "0x%lx", args[sc->offset]); 700181061Sdes break; 701181061Sdes case Readlinkres: { 702181061Sdes char *tmp2; 703181061Sdes if (retval == -1) { 704181061Sdes tmp = strdup(""); 705181061Sdes break; 706181061Sdes } 707181061Sdes tmp2 = get_string(pid, (void*)args[sc->offset], retval); 708181061Sdes asprintf(&tmp, "\"%s\"", tmp2); 709181061Sdes free(tmp2); 710181061Sdes break; 711181061Sdes } 712181061Sdes case Ioctl: { 713181061Sdes const char *temp = ioctlname(args[sc->offset]); 714240005Szont if (temp) 715181061Sdes tmp = strdup(temp); 716240005Szont else { 717181061Sdes unsigned long arg = args[sc->offset]; 718240005Szont asprintf(&tmp, "0x%lx { IO%s%s 0x%lx('%c'), %lu, %lu }", 719240005Szont arg, arg & IOC_OUT ? "R" : "", 720240005Szont arg & IOC_IN ? "W" : "", IOCGROUP(arg), 721240005Szont isprint(IOCGROUP(arg)) ? (char)IOCGROUP(arg) : '?', 722181061Sdes arg & 0xFF, IOCPARM_LEN(arg)); 723181061Sdes } 724181061Sdes break; 725181061Sdes } 726181061Sdes case Umtx: { 727181061Sdes struct umtx umtx; 728240005Szont if (get_struct(pid, (void *)args[sc->offset], &umtx, 729240005Szont sizeof(umtx)) != -1) 730181061Sdes asprintf(&tmp, "{ 0x%lx }", (long)umtx.u_owner); 731181061Sdes else 732181061Sdes asprintf(&tmp, "0x%lx", args[sc->offset]); 733181061Sdes break; 734181061Sdes } 735181061Sdes case Timespec: { 736181061Sdes struct timespec ts; 737240005Szont if (get_struct(pid, (void *)args[sc->offset], &ts, 738240005Szont sizeof(ts)) != -1) 739240005Szont asprintf(&tmp, "{%ld.%09ld }", (long)ts.tv_sec, 740240005Szont ts.tv_nsec); 741181061Sdes else 742181061Sdes asprintf(&tmp, "0x%lx", args[sc->offset]); 743181061Sdes break; 744181061Sdes } 745181061Sdes case Timeval: { 746181061Sdes struct timeval tv; 747240005Szont if (get_struct(pid, (void *)args[sc->offset], &tv, sizeof(tv)) 748240005Szont != -1) 749240005Szont asprintf(&tmp, "{%ld.%06ld }", (long)tv.tv_sec, 750240005Szont tv.tv_usec); 751181061Sdes else 752181061Sdes asprintf(&tmp, "0x%lx", args[sc->offset]); 753181061Sdes break; 754181061Sdes } 755181061Sdes case Timeval2: { 756181061Sdes struct timeval tv[2]; 757240005Szont if (get_struct(pid, (void *)args[sc->offset], &tv, sizeof(tv)) 758240005Szont != -1) 759181061Sdes asprintf(&tmp, "{%ld.%06ld, %ld.%06ld }", 760181061Sdes (long)tv[0].tv_sec, tv[0].tv_usec, 761181061Sdes (long)tv[1].tv_sec, tv[1].tv_usec); 762181061Sdes else 763181061Sdes asprintf(&tmp, "0x%lx", args[sc->offset]); 764181061Sdes break; 765181061Sdes } 766181061Sdes case Itimerval: { 767181061Sdes struct itimerval itv; 768240005Szont if (get_struct(pid, (void *)args[sc->offset], &itv, 769240005Szont sizeof(itv)) != -1) 770181061Sdes asprintf(&tmp, "{%ld.%06ld, %ld.%06ld }", 771181061Sdes (long)itv.it_interval.tv_sec, 772181061Sdes itv.it_interval.tv_usec, 773181061Sdes (long)itv.it_value.tv_sec, 774181061Sdes itv.it_value.tv_usec); 775181061Sdes else 776181061Sdes asprintf(&tmp, "0x%lx", args[sc->offset]); 777181061Sdes break; 778181061Sdes } 779181061Sdes case Pollfd: { 780181061Sdes /* 781240005Szont * XXX: A Pollfd argument expects the /next/ syscall argument 782240005Szont * to be the number of fds in the array. This matches the poll 783240005Szont * syscall. 784181061Sdes */ 785181061Sdes struct pollfd *pfd; 786181061Sdes int numfds = args[sc->offset+1]; 787181061Sdes int bytes = sizeof(struct pollfd) * numfds; 788181061Sdes int i, tmpsize, u, used; 789181061Sdes const int per_fd = 100; 790127332Sdwmalone 791181061Sdes if ((pfd = malloc(bytes)) == NULL) 792240005Szont err(1, "Cannot malloc %d bytes for pollfd array", 793240005Szont bytes); 794240005Szont if (get_struct(pid, (void *)args[sc->offset], pfd, bytes) 795240005Szont != -1) { 796181061Sdes used = 0; 797181061Sdes tmpsize = 1 + per_fd * numfds + 2; 798181061Sdes if ((tmp = malloc(tmpsize)) == NULL) 799240005Szont err(1, "Cannot alloc %d bytes for poll output", 800240005Szont tmpsize); 801127332Sdwmalone 802181061Sdes tmp[used++] = '{'; 803181061Sdes for (i = 0; i < numfds; i++) { 804127332Sdwmalone 805240005Szont u = snprintf(tmp + used, per_fd, "%s%d/%s", 806240005Szont i > 0 ? " " : "", pfd[i].fd, 807240005Szont xlookup_bits(poll_flags, pfd[i].events)); 808181061Sdes if (u > 0) 809181061Sdes used += u < per_fd ? u : per_fd; 810181061Sdes } 811181061Sdes tmp[used++] = '}'; 812181061Sdes tmp[used++] = '\0'; 813181061Sdes } else { 814181061Sdes asprintf(&tmp, "0x%lx", args[sc->offset]); 815181061Sdes } 816181061Sdes free(pfd); 817181061Sdes break; 818127332Sdwmalone } 819181061Sdes case Fd_set: { 820181061Sdes /* 821240005Szont * XXX: A Fd_set argument expects the /first/ syscall argument 822240005Szont * to be the number of fds in the array. This matches the 823240005Szont * select syscall. 824181061Sdes */ 825181061Sdes fd_set *fds; 826181061Sdes int numfds = args[0]; 827181061Sdes int bytes = _howmany(numfds, _NFDBITS) * _NFDBITS; 828181061Sdes int i, tmpsize, u, used; 829181061Sdes const int per_fd = 20; 830127332Sdwmalone 831181061Sdes if ((fds = malloc(bytes)) == NULL) 832240005Szont err(1, "Cannot malloc %d bytes for fd_set array", 833240005Szont bytes); 834240005Szont if (get_struct(pid, (void *)args[sc->offset], fds, bytes) 835240005Szont != -1) { 836181061Sdes used = 0; 837181061Sdes tmpsize = 1 + numfds * per_fd + 2; 838181061Sdes if ((tmp = malloc(tmpsize)) == NULL) 839240005Szont err(1, "Cannot alloc %d bytes for fd_set " 840240005Szont "output", tmpsize); 841127332Sdwmalone 842181061Sdes tmp[used++] = '{'; 843181061Sdes for (i = 0; i < numfds; i++) { 844181061Sdes if (FD_ISSET(i, fds)) { 845240005Szont u = snprintf(tmp + used, per_fd, "%d ", 846240005Szont i); 847181061Sdes if (u > 0) 848181061Sdes used += u < per_fd ? u : per_fd; 849181061Sdes } 850181061Sdes } 851181061Sdes if (tmp[used-1] == ' ') 852181061Sdes used--; 853181061Sdes tmp[used++] = '}'; 854181061Sdes tmp[used++] = '\0'; 855240005Szont } else 856181061Sdes asprintf(&tmp, "0x%lx", args[sc->offset]); 857181061Sdes free(fds); 858181061Sdes break; 859127332Sdwmalone } 860255493Sjhb case Signal: 861255493Sjhb tmp = strsig2(args[sc->offset]); 862181061Sdes break; 863181061Sdes case Sigset: { 864181061Sdes long sig; 865181061Sdes sigset_t ss; 866181061Sdes int i, used; 867255493Sjhb char *signame; 868158630Spav 869181061Sdes sig = args[sc->offset]; 870240005Szont if (get_struct(pid, (void *)args[sc->offset], (void *)&ss, 871240005Szont sizeof(ss)) == -1) { 872181061Sdes asprintf(&tmp, "0x%lx", args[sc->offset]); 873181061Sdes break; 874181061Sdes } 875181061Sdes tmp = malloc(sys_nsig * 8); /* 7 bytes avg per signal name */ 876181061Sdes used = 0; 877181061Sdes for (i = 1; i < sys_nsig; i++) { 878255493Sjhb if (sigismember(&ss, i)) { 879255493Sjhb signame = strsig(i); 880255493Sjhb used += sprintf(tmp + used, "%s|", signame); 881255493Sjhb free(signame); 882255493Sjhb } 883181061Sdes } 884181061Sdes if (used) 885181061Sdes tmp[used-1] = 0; 886181061Sdes else 887181061Sdes strcpy(tmp, "0x0"); 888181061Sdes break; 889181061Sdes } 890181061Sdes case Sigprocmask: { 891181061Sdes switch (args[sc->offset]) { 892240005Szont#define S(a) case a: tmp = strdup(#a); break; 893181061Sdes S(SIG_BLOCK); 894181061Sdes S(SIG_UNBLOCK); 895181061Sdes S(SIG_SETMASK); 896127328Salfred#undef S 897181061Sdes } 898181061Sdes if (tmp == NULL) 899181061Sdes asprintf(&tmp, "0x%lx", args[sc->offset]); 900181061Sdes break; 901127328Salfred } 902181061Sdes case Fcntlflag: { 903181061Sdes /* XXX output depends on the value of the previous argument */ 904181061Sdes switch (args[sc->offset-1]) { 905181061Sdes case F_SETFD: 906240005Szont tmp = strdup(xlookup_bits(fcntlfd_arg, 907240005Szont args[sc->offset])); 908181061Sdes break; 909181061Sdes case F_SETFL: 910240005Szont tmp = strdup(xlookup_bits(fcntlfl_arg, 911240005Szont args[sc->offset])); 912181061Sdes break; 913181061Sdes case F_GETFD: 914181061Sdes case F_GETFL: 915181061Sdes case F_GETOWN: 916181061Sdes tmp = strdup(""); 917181061Sdes break; 918181061Sdes default: 919181061Sdes asprintf(&tmp, "0x%lx", args[sc->offset]); 920181061Sdes break; 921181061Sdes } 922181061Sdes break; 923181061Sdes } 924181061Sdes case Open: 925181061Sdes tmp = strdup(xlookup_bits(open_flags, args[sc->offset])); 926181061Sdes break; 927181061Sdes case Fcntl: 928181061Sdes tmp = strdup(xlookup(fcntl_arg, args[sc->offset])); 929181061Sdes break; 930181061Sdes case Mprot: 931181061Sdes tmp = strdup(xlookup_bits(mprot_flags, args[sc->offset])); 932181061Sdes break; 933254430Sjhb case Mmapflags: { 934254538Sjhb char *base, *alignstr; 935254430Sjhb int align, flags; 936254430Sjhb 937254430Sjhb /* 938254430Sjhb * MAP_ALIGNED can't be handled by xlookup_bits(), so 939254430Sjhb * generate that string manually and prepend it to the 940254430Sjhb * string from xlookup_bits(). Have to be careful to 941254430Sjhb * avoid outputting MAP_ALIGNED|0 if MAP_ALIGNED is 942254430Sjhb * the only flag. 943254430Sjhb */ 944254430Sjhb flags = args[sc->offset] & ~MAP_ALIGNMENT_MASK; 945254430Sjhb align = args[sc->offset] & MAP_ALIGNMENT_MASK; 946254430Sjhb if (align != 0) { 947254430Sjhb if (align == MAP_ALIGNED_SUPER) 948254430Sjhb alignstr = strdup("MAP_ALIGNED_SUPER"); 949254430Sjhb else 950254430Sjhb asprintf(&alignstr, "MAP_ALIGNED(%d)", 951254430Sjhb align >> MAP_ALIGNMENT_SHIFT); 952254430Sjhb if (flags == 0) { 953254430Sjhb tmp = alignstr; 954254430Sjhb break; 955254430Sjhb } 956254430Sjhb } else 957254430Sjhb alignstr = NULL; 958254430Sjhb base = strdup(xlookup_bits(mmap_flags, flags)); 959254430Sjhb if (alignstr == NULL) { 960254430Sjhb tmp = base; 961254430Sjhb break; 962254430Sjhb } 963254430Sjhb asprintf(&tmp, "%s|%s", alignstr, base); 964254430Sjhb free(alignstr); 965254430Sjhb free(base); 966181061Sdes break; 967254430Sjhb } 968181061Sdes case Whence: 969181061Sdes tmp = strdup(xlookup(whence_arg, args[sc->offset])); 970181061Sdes break; 971181061Sdes case Sockdomain: 972181061Sdes tmp = strdup(xlookup(sockdomain_arg, args[sc->offset])); 973181061Sdes break; 974181061Sdes case Socktype: 975181061Sdes tmp = strdup(xlookup(socktype_arg, args[sc->offset])); 976181061Sdes break; 977181061Sdes case Shutdown: 978181061Sdes tmp = strdup(xlookup(shutdown_arg, args[sc->offset])); 979181061Sdes break; 980181061Sdes case Resource: 981181061Sdes tmp = strdup(xlookup(resource_arg, args[sc->offset])); 982181061Sdes break; 983181061Sdes case Pathconf: 984181061Sdes tmp = strdup(xlookup(pathconf_arg, args[sc->offset])); 985181061Sdes break; 986253850Smarkj case Rforkflags: 987253850Smarkj tmp = strdup(xlookup_bits(rfork_flags, args[sc->offset])); 988253850Smarkj break; 989181061Sdes case Sockaddr: { 990181061Sdes struct sockaddr_storage ss; 991181061Sdes char addr[64]; 992181061Sdes struct sockaddr_in *lsin; 993181061Sdes struct sockaddr_in6 *lsin6; 994181061Sdes struct sockaddr_un *sun; 995181061Sdes struct sockaddr *sa; 996181061Sdes char *p; 997181061Sdes u_char *q; 998181061Sdes int i; 99985292Sdes 1000181061Sdes if (args[sc->offset] == 0) { 1001181061Sdes asprintf(&tmp, "NULL"); 1002181061Sdes break; 1003181061Sdes } 1004121606Smarcel 1005181061Sdes /* yuck: get ss_len */ 1006181061Sdes if (get_struct(pid, (void *)args[sc->offset], (void *)&ss, 1007240005Szont sizeof(ss.ss_len) + sizeof(ss.ss_family)) == -1) 1008181061Sdes err(1, "get_struct %p", (void *)args[sc->offset]); 1009181061Sdes /* 1010181061Sdes * If ss_len is 0, then try to guess from the sockaddr type. 1011181061Sdes * AF_UNIX may be initialized incorrectly, so always frob 1012181061Sdes * it by using the "right" size. 1013181061Sdes */ 1014181061Sdes if (ss.ss_len == 0 || ss.ss_family == AF_UNIX) { 1015181061Sdes switch (ss.ss_family) { 1016181061Sdes case AF_INET: 1017181061Sdes ss.ss_len = sizeof(*lsin); 1018181061Sdes break; 1019181061Sdes case AF_UNIX: 1020181061Sdes ss.ss_len = sizeof(*sun); 1021181061Sdes break; 1022181061Sdes default: 1023181061Sdes /* hurrrr */ 1024181061Sdes break; 1025181061Sdes } 1026181061Sdes } 1027240005Szont if (get_struct(pid, (void *)args[sc->offset], (void *)&ss, 1028240005Szont ss.ss_len) == -1) { 1029181061Sdes err(2, "get_struct %p", (void *)args[sc->offset]); 1030181061Sdes } 103185292Sdes 1032181061Sdes switch (ss.ss_family) { 1033181061Sdes case AF_INET: 1034181061Sdes lsin = (struct sockaddr_in *)&ss; 1035181061Sdes inet_ntop(AF_INET, &lsin->sin_addr, addr, sizeof addr); 1036240005Szont asprintf(&tmp, "{ AF_INET %s:%d }", addr, 1037240005Szont htons(lsin->sin_port)); 1038181061Sdes break; 1039181061Sdes case AF_INET6: 1040181061Sdes lsin6 = (struct sockaddr_in6 *)&ss; 1041240005Szont inet_ntop(AF_INET6, &lsin6->sin6_addr, addr, 1042240005Szont sizeof addr); 1043240005Szont asprintf(&tmp, "{ AF_INET6 [%s]:%d }", addr, 1044240005Szont htons(lsin6->sin6_port)); 1045181061Sdes break; 1046181061Sdes case AF_UNIX: 1047181061Sdes sun = (struct sockaddr_un *)&ss; 1048181061Sdes asprintf(&tmp, "{ AF_UNIX \"%s\" }", sun->sun_path); 1049181061Sdes break; 1050181061Sdes default: 1051181061Sdes sa = (struct sockaddr *)&ss; 1052240005Szont asprintf(&tmp, "{ sa_len = %d, sa_family = %d, sa_data " 1053240005Szont "= {%n%*s } }", (int)sa->sa_len, (int)sa->sa_family, 1054240005Szont &i, 6 * (int)(sa->sa_len - ((char *)&sa->sa_data - 1055240005Szont (char *)sa)), ""); 1056181061Sdes if (tmp != NULL) { 1057181061Sdes p = tmp + i; 1058240005Szont for (q = (u_char *)&sa->sa_data; 1059240005Szont q < (u_char *)sa + sa->sa_len; q++) 1060181061Sdes p += sprintf(p, " %#02x,", *q); 1061181061Sdes } 1062181061Sdes } 1063181061Sdes break; 106486138Sgreen } 1065181061Sdes case Sigaction: { 1066181061Sdes struct sigaction sa; 1067181061Sdes char *hand; 1068181061Sdes const char *h; 1069127332Sdwmalone 1070240005Szont if (get_struct(pid, (void *)args[sc->offset], &sa, sizeof(sa)) 1071240005Szont != -1) { 1072181061Sdes asprintf(&hand, "%p", sa.sa_handler); 1073181061Sdes if (sa.sa_handler == SIG_DFL) 1074181061Sdes h = "SIG_DFL"; 1075181061Sdes else if (sa.sa_handler == SIG_IGN) 1076181061Sdes h = "SIG_IGN"; 1077181061Sdes else 1078181061Sdes h = hand; 1079158630Spav 1080240005Szont asprintf(&tmp, "{ %s %s ss_t }", h, 1081181061Sdes xlookup_bits(sigaction_flags, sa.sa_flags)); 1082181061Sdes free(hand); 1083240005Szont } else 1084181061Sdes asprintf(&tmp, "0x%lx", args[sc->offset]); 1085181061Sdes break; 1086181061Sdes } 1087181061Sdes case Kevent: { 1088181061Sdes /* 1089181061Sdes * XXX XXX: the size of the array is determined by either the 1090181061Sdes * next syscall argument, or by the syscall returnvalue, 1091181061Sdes * depending on which argument number we are. This matches the 1092181061Sdes * kevent syscall, but luckily that's the only syscall that uses 1093181061Sdes * them. 1094181061Sdes */ 1095181061Sdes struct kevent *ke; 1096181061Sdes int numevents = -1; 1097181061Sdes int bytes = 0; 1098181061Sdes int i, tmpsize, u, used; 1099181061Sdes const int per_ke = 100; 1100158630Spav 1101181061Sdes if (sc->offset == 1) 1102181061Sdes numevents = args[sc->offset+1]; 1103181061Sdes else if (sc->offset == 3 && retval != -1) 1104181061Sdes numevents = retval; 1105158630Spav 1106181061Sdes if (numevents >= 0) 1107181061Sdes bytes = sizeof(struct kevent) * numevents; 1108181061Sdes if ((ke = malloc(bytes)) == NULL) 1109240005Szont err(1, "Cannot malloc %d bytes for kevent array", 1110240005Szont bytes); 1111240005Szont if (numevents >= 0 && get_struct(pid, (void *)args[sc->offset], 1112240005Szont ke, bytes) != -1) { 1113181061Sdes used = 0; 1114181061Sdes tmpsize = 1 + per_ke * numevents + 2; 1115181061Sdes if ((tmp = malloc(tmpsize)) == NULL) 1116240005Szont err(1, "Cannot alloc %d bytes for kevent " 1117240005Szont "output", tmpsize); 1118158630Spav 1119181061Sdes tmp[used++] = '{'; 1120181061Sdes for (i = 0; i < numevents; i++) { 1121181061Sdes u = snprintf(tmp + used, per_ke, 1122181061Sdes "%s%p,%s,%s,%d,%p,%p", 1123181061Sdes i > 0 ? " " : "", 1124181061Sdes (void *)ke[i].ident, 1125181061Sdes xlookup(kevent_filters, ke[i].filter), 1126181061Sdes xlookup_bits(kevent_flags, ke[i].flags), 1127181061Sdes ke[i].fflags, 1128181061Sdes (void *)ke[i].data, 1129181061Sdes (void *)ke[i].udata); 1130181061Sdes if (u > 0) 1131181061Sdes used += u < per_ke ? u : per_ke; 1132181061Sdes } 1133181061Sdes tmp[used++] = '}'; 1134181061Sdes tmp[used++] = '\0'; 1135181061Sdes } else { 1136181061Sdes asprintf(&tmp, "0x%lx", args[sc->offset]); 1137181061Sdes } 1138181061Sdes free(ke); 1139181061Sdes break; 1140158630Spav } 1141181061Sdes case Stat: { 1142181061Sdes struct stat st; 1143240005Szont if (get_struct(pid, (void *)args[sc->offset], &st, sizeof(st)) 1144240005Szont != -1) { 1145181061Sdes char mode[12]; 1146181061Sdes strmode(st.st_mode, mode); 1147240005Szont asprintf(&tmp, 1148240005Szont "{ mode=%s,inode=%jd,size=%jd,blksize=%ld }", mode, 1149240005Szont (intmax_t)st.st_ino, (intmax_t)st.st_size, 1150240005Szont (long)st.st_blksize); 1151181061Sdes } else { 1152181061Sdes asprintf(&tmp, "0x%lx", args[sc->offset]); 1153181061Sdes } 1154181061Sdes break; 1155181061Sdes } 1156181061Sdes case Rusage: { 1157181061Sdes struct rusage ru; 1158240005Szont if (get_struct(pid, (void *)args[sc->offset], &ru, sizeof(ru)) 1159240005Szont != -1) { 1160240005Szont asprintf(&tmp, 1161240005Szont "{ u=%ld.%06ld,s=%ld.%06ld,in=%ld,out=%ld }", 1162181061Sdes (long)ru.ru_utime.tv_sec, ru.ru_utime.tv_usec, 1163181061Sdes (long)ru.ru_stime.tv_sec, ru.ru_stime.tv_usec, 1164181061Sdes ru.ru_inblock, ru.ru_oublock); 1165240005Szont } else 1166181061Sdes asprintf(&tmp, "0x%lx", args[sc->offset]); 1167181061Sdes break; 1168181061Sdes } 1169181061Sdes case Rlimit: { 1170181061Sdes struct rlimit rl; 1171240005Szont if (get_struct(pid, (void *)args[sc->offset], &rl, sizeof(rl)) 1172240005Szont != -1) { 1173181061Sdes asprintf(&tmp, "{ cur=%ju,max=%ju }", 1174181061Sdes rl.rlim_cur, rl.rlim_max); 1175240005Szont } else 1176181061Sdes asprintf(&tmp, "0x%lx", args[sc->offset]); 1177181061Sdes break; 1178181061Sdes } 1179255493Sjhb case ExitStatus: { 1180255493Sjhb char *signame; 1181255493Sjhb int status; 1182255493Sjhb signame = NULL; 1183255493Sjhb if (get_struct(pid, (void *)args[sc->offset], &status, 1184255493Sjhb sizeof(status)) != -1) { 1185255493Sjhb if (WIFCONTINUED(status)) 1186255493Sjhb tmp = strdup("{ CONTINUED }"); 1187255493Sjhb else if (WIFEXITED(status)) 1188255493Sjhb asprintf(&tmp, "{ EXITED,val=%d }", 1189255493Sjhb WEXITSTATUS(status)); 1190255493Sjhb else if (WIFSIGNALED(status)) 1191255493Sjhb asprintf(&tmp, "{ SIGNALED,sig=%s%s }", 1192255493Sjhb signame = strsig2(WTERMSIG(status)), 1193255493Sjhb WCOREDUMP(status) ? ",cored" : ""); 1194255493Sjhb else 1195255493Sjhb asprintf(&tmp, "{ STOPPED,sig=%s }", 1196255493Sjhb signame = strsig2(WTERMSIG(status))); 1197255493Sjhb } else 1198255493Sjhb asprintf(&tmp, "0x%lx", args[sc->offset]); 1199255493Sjhb free(signame); 1200255493Sjhb break; 1201255493Sjhb } 1202255493Sjhb case Waitoptions: 1203255493Sjhb tmp = strdup(xlookup_bits(wait_options, args[sc->offset])); 1204255493Sjhb break; 1205255493Sjhb case Idtype: 1206255493Sjhb tmp = strdup(xlookup(idtype_arg, args[sc->offset])); 1207255493Sjhb break; 1208255708Sjhb case Procctl: 1209255708Sjhb tmp = strdup(xlookup(procctl_arg, args[sc->offset])); 1210255708Sjhb break; 1211181061Sdes default: 1212181061Sdes errx(1, "Invalid argument type %d\n", sc->type & ARG_MASK); 1213181061Sdes } 1214181061Sdes return (tmp); 121531567Ssef} 121631567Ssef 121731567Ssef/* 121831567Ssef * print_syscall 121931567Ssef * Print (to outfile) the system call and its arguments. Note that 122031567Ssef * nargs is the number of arguments (not the number of words; this is 122131567Ssef * potentially confusing, I know). 122231567Ssef */ 122331567Ssef 122431567Ssefvoid 1225240005Szontprint_syscall(struct trussinfo *trussinfo, const char *name, int nargs, 1226240005Szont char **s_args) 1227181061Sdes{ 1228181061Sdes struct timespec timediff; 1229240005Szont int i, len; 1230101283Smdodd 1231240005Szont len = 0; 1232181061Sdes if (trussinfo->flags & FOLLOWFORKS) 1233181061Sdes len += fprintf(trussinfo->outfile, "%5d: ", trussinfo->pid); 1234101283Smdodd 1235240562Szont if (name != NULL && (strcmp(name, "execve") == 0 || 1236240005Szont strcmp(name, "exit") == 0)) { 1237240562Szont clock_gettime(CLOCK_REALTIME, &trussinfo->curthread->after); 1238181061Sdes } 1239101285Smdodd 1240181061Sdes if (trussinfo->flags & ABSOLUTETIMESTAMPS) { 1241247338Sdelphij timespecsubt(&trussinfo->curthread->after, 1242240562Szont &trussinfo->start_time, &timediff); 1243181061Sdes len += fprintf(trussinfo->outfile, "%ld.%09ld ", 1244181061Sdes (long)timediff.tv_sec, timediff.tv_nsec); 1245181061Sdes } 1246101285Smdodd 1247181061Sdes if (trussinfo->flags & RELATIVETIMESTAMPS) { 1248247338Sdelphij timespecsubt(&trussinfo->curthread->after, 1249240562Szont &trussinfo->curthread->before, &timediff); 1250181061Sdes len += fprintf(trussinfo->outfile, "%ld.%09ld ", 1251181061Sdes (long)timediff.tv_sec, timediff.tv_nsec); 1252181061Sdes } 1253101285Smdodd 1254181061Sdes len += fprintf(trussinfo->outfile, "%s(", name); 1255101283Smdodd 1256181061Sdes for (i = 0; i < nargs; i++) { 1257181061Sdes if (s_args[i]) 1258181061Sdes len += fprintf(trussinfo->outfile, "%s", s_args[i]); 1259181061Sdes else 1260240005Szont len += fprintf(trussinfo->outfile, 1261240005Szont "<missing argument>"); 1262240005Szont len += fprintf(trussinfo->outfile, "%s", i < (nargs - 1) ? 1263240005Szont "," : ""); 1264181061Sdes } 1265181061Sdes len += fprintf(trussinfo->outfile, ")"); 1266181061Sdes for (i = 0; i < 6 - (len / 8); i++) 1267181061Sdes fprintf(trussinfo->outfile, "\t"); 126831567Ssef} 126958224Ssef 127058224Ssefvoid 1271122348Smarcelprint_syscall_ret(struct trussinfo *trussinfo, const char *name, int nargs, 1272192025Sdds char **s_args, int errorp, long retval, struct syscall *sc) 1273122348Smarcel{ 1274192025Sdds struct timespec timediff; 1275181061Sdes 1276192025Sdds if (trussinfo->flags & COUNTONLY) { 1277192025Sdds if (!sc) 1278192025Sdds return; 1279240562Szont clock_gettime(CLOCK_REALTIME, &trussinfo->curthread->after); 1280247338Sdelphij timespecsubt(&trussinfo->curthread->after, 1281240562Szont &trussinfo->curthread->before, &timediff); 1282247338Sdelphij timespecadd(&sc->time, &timediff, &sc->time); 1283192025Sdds sc->ncalls++; 1284192025Sdds if (errorp) 1285192025Sdds sc->nerror++; 1286192025Sdds return; 1287192025Sdds } 1288192025Sdds 1289181061Sdes print_syscall(trussinfo, name, nargs, s_args); 1290181061Sdes fflush(trussinfo->outfile); 1291240005Szont if (errorp) 1292240005Szont fprintf(trussinfo->outfile, " ERR#%ld '%s'\n", retval, 1293240005Szont strerror(retval)); 1294240005Szont else { 1295200780Sjh /* 1296200780Sjh * Because pipe(2) has a special assembly glue to provide the 1297200780Sjh * libc API, we have to adjust retval. 1298200780Sjh */ 1299240005Szont if (name != NULL && strcmp(name, "pipe") == 0) 1300200780Sjh retval = 0; 1301181061Sdes fprintf(trussinfo->outfile, " = %ld (0x%lx)\n", retval, retval); 1302181061Sdes } 130358224Ssef} 1304192025Sdds 1305192025Sddsvoid 1306192025Sddsprint_summary(struct trussinfo *trussinfo) 1307192025Sdds{ 1308240005Szont struct timespec total = {0, 0}; 1309192025Sdds struct syscall *sc; 1310192025Sdds int ncall, nerror; 1311192025Sdds 1312192025Sdds fprintf(trussinfo->outfile, "%-20s%15s%8s%8s\n", 1313240005Szont "syscall", "seconds", "calls", "errors"); 1314192025Sdds ncall = nerror = 0; 1315192025Sdds for (sc = syscalls; sc->name != NULL; sc++) 1316192025Sdds if (sc->ncalls) { 1317200781Sjh fprintf(trussinfo->outfile, "%-20s%5jd.%09ld%8d%8d\n", 1318200781Sjh sc->name, (intmax_t)sc->time.tv_sec, 1319200781Sjh sc->time.tv_nsec, sc->ncalls, sc->nerror); 1320247338Sdelphij timespecadd(&total, &sc->time, &total); 1321192025Sdds ncall += sc->ncalls; 1322192025Sdds nerror += sc->nerror; 1323192025Sdds } 1324192025Sdds fprintf(trussinfo->outfile, "%20s%15s%8s%8s\n", 1325240005Szont "", "-------------", "-------", "-------"); 1326200781Sjh fprintf(trussinfo->outfile, "%-20s%5jd.%09ld%8d%8d\n", 1327240005Szont "", (intmax_t)total.tv_sec, total.tv_nsec, ncall, nerror); 1328192025Sdds} 1329