- Add decoding of kse_release, kevent, sigprocmask, unmount, socket, getrusage,

rename, __getcwd, shutdown, getrlimit, setrlimit, _umtx_lock, _umtx_unlock,
  pathconf, truncate, ftruncate, kill

- Decode more arguments of open, mprot, *stat, and fcntl.

- Convert all constant-macro and bitfield decoding to lookup tables; much
  cleaner than previous code.

- Print the timestamp of process exit and signal reception when -d or -D are in
  use

- Try six times with 1/2 second delay to debug the child

PR:		bin/52190 (updated)
Submitted by:	Dan Nelson <dnelson@allantgroup.com>
Approved by:	alfred
This commit is contained in:
Pav Lucistnik 2006-05-15 21:18:28 +00:00
parent c46cb391cb
commit 081e5c4890
7 changed files with 579 additions and 154 deletions

View File

@ -180,7 +180,8 @@ i386_syscall_entry(struct trussinfo *trussinfo, int nargs) {
if (read(Procfd, fsc.args, nargs * sizeof(unsigned long)) == -1)
return;
sc = get_syscall(fsc.name);
if (fsc.name)
sc = get_syscall(fsc.name);
if (sc) {
fsc.nargs = sc->nargs;
} else {
@ -316,7 +317,7 @@ i386_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused)
char *temp;
if (sc->args[i].type & OUT) {
/*
* If an error occurred, than don't bothe getting the data;
* If an error occurred, then don't bother getting the data;
* it may not be valid.
*/
if (errorp)
@ -328,6 +329,19 @@ i386_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused)
}
}
/*
* The pipe syscall returns its fds in two registers and has assembly glue
* to provide the libc API, so it cannot be handled like regular syscalls.
* The nargs check is so we don't have to do yet another strcmp on every
* syscall.
*/
if (!errorp && fsc.nargs == 0 && fsc.name && strcmp(fsc.name, "pipe") == 0) {
fsc.nargs = 1;
fsc.s_args = malloc((1+fsc.nargs) * sizeof(char*));
asprintf(&fsc.s_args[0], "[%d,%d]", (int)retval, regs.r_edx);
retval = 0;
}
/*
* It would probably be a good idea to merge the error handling,
* but that complicates things considerably.

View File

@ -180,7 +180,8 @@ i386_syscall_entry(struct trussinfo *trussinfo, int nargs) {
if (read(Procfd, fsc.args, nargs * sizeof(unsigned long)) == -1)
return;
sc = get_syscall(fsc.name);
if (fsc.name)
sc = get_syscall(fsc.name);
if (sc) {
fsc.nargs = sc->nargs;
} else {
@ -316,7 +317,7 @@ i386_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused)
char *temp;
if (sc->args[i].type & OUT) {
/*
* If an error occurred, than don't bothe getting the data;
* If an error occurred, then don't bother getting the data;
* it may not be valid.
*/
if (errorp)
@ -328,6 +329,19 @@ i386_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused)
}
}
/*
* The pipe syscall returns its fds in two registers and has assembly glue
* to provide the libc API, so it cannot be handled like regular syscalls.
* The nargs check is so we don't have to do yet another strcmp on every
* syscall.
*/
if (!errorp && fsc.nargs == 0 && fsc.name && strcmp(fsc.name, "pipe") == 0) {
fsc.nargs = 1;
fsc.s_args = malloc((1+fsc.nargs) * sizeof(char*));
asprintf(&fsc.s_args[0], "[%d,%d]", (int)retval, regs.r_edx);
retval = 0;
}
/*
* It would probably be a good idea to merge the error handling,
* but that complicates things considerably.

View File

@ -279,6 +279,7 @@ main(int ac, char **av)
do {
int val = 0;
struct timespec timediff;
if (ioctl(Procfd, PIOCWAIT, &pfs) == -1)
warn("PIOCWAIT top of loop");
@ -328,6 +329,23 @@ main(int ac, char **av)
funcs->exit_syscall(trussinfo, pfs.val);
break;
case S_SIG:
if (trussinfo->flags & FOLLOWFORKS)
fprintf(trussinfo->outfile, "%5d: ",
trussinfo->pid);
if (trussinfo->flags & ABSOLUTETIMESTAMPS) {
timespecsubt(&trussinfo->after,
&trussinfo->start_time, &timediff);
fprintf(trussinfo->outfile, "%ld.%09ld ",
(long)timediff.tv_sec,
timediff.tv_nsec);
}
if (trussinfo->flags & RELATIVETIMESTAMPS) {
timespecsubt(&trussinfo->after,
&trussinfo->before, &timediff);
fprintf(trussinfo->outfile, "%ld.%09ld ",
(long)timediff.tv_sec,
timediff.tv_nsec);
}
signame = strsig(pfs.val);
fprintf(trussinfo->outfile,
"SIGNAL %lu (%s)\n", pfs.val,
@ -336,6 +354,22 @@ main(int ac, char **av)
sigexit = pfs.val;
break;
case S_EXIT:
if (trussinfo->flags & FOLLOWFORKS)
fprintf(trussinfo->outfile, "%5d: ",
trussinfo->pid);
if (trussinfo->flags & ABSOLUTETIMESTAMPS) {
timespecsubt(&trussinfo->after,
&trussinfo->start_time, &timediff);
fprintf(trussinfo->outfile, "%ld.%09ld ",
(long)timediff.tv_sec,
timediff.tv_nsec);
}
if (trussinfo->flags & RELATIVETIMESTAMPS) {
timespecsubt(&trussinfo->after,
&trussinfo->before, &timediff);
fprintf(trussinfo->outfile, "%ld.%09ld ",
(long)timediff.tv_sec, timediff.tv_nsec);
}
fprintf(trussinfo->outfile,
"process exit, rval = %lu\n", pfs.val);
break;

View File

@ -71,6 +71,7 @@ setup_and_wait(char *command[])
int fd;
int pid;
int flags;
int loop;
pid = fork();
if (pid == -1) {
@ -108,15 +109,30 @@ setup_and_wait(char *command[])
}
sprintf(buf, "/proc/%d/mem", pid);
if ((fd = open(buf, O_RDWR)) == -1)
err(5, "cannot open %s", buf);
if (ioctl(fd, PIOCWAIT, &pfs) == -1)
err(6, "PIOCWAIT");
if (pfs.why == S_EXIT) {
warnx("process exited before exec'ing");
ioctl(fd, PIOCCONT, 0);
wait(0);
exit(7);
/* Try 6 times to trace our child, waiting 1/2 second each time */
for (loop=6 ;; loop--) {
if (loop != 6)
usleep(500000);
if ((fd = open(buf, O_RDWR)) == -1) {
if (loop > 0)
continue;
else
err(5, "cannot open1 %s", buf);
}
if (ioctl(fd, PIOCWAIT, &pfs) == -1) {
if (loop >= 0)
continue;
else
err(6, "PIOCWAIT");
}
if (pfs.why == S_EXIT) {
warnx("process exited before exec'ing");
ioctl(fd, PIOCCONT, 0);
wait(0);
exit(7);
} else
break;
}
close(fd);
return (pid);
@ -136,6 +152,7 @@ start_tracing(int pid, int failisfatal, int eventflags, int flags)
struct procfs_status tmp;
sprintf(buf, "/proc/%d/mem", pid);
/* usleep(500000); */
fd = open(buf, O_RDWR);
if (fd == -1) {
@ -146,7 +163,7 @@ start_tracing(int pid, int failisfatal, int eventflags, int flags)
*/
if (!failisfatal && kill(pid, 0) == -1)
return (-1);
err(8, "cannot open %s", buf);
err(8, "cannot open2 %s", buf);
}
if (ioctl(fd, PIOCSTATUS, &tmp) == -1) {

View File

@ -5,11 +5,10 @@
* Hex -- values that should be printed in hex (addresses)
* Octal -- Same as above, but octal
* Int -- normal integer values (file descriptors, for example)
* String -- pointers to sensible data. Note that we treat read() and
* write() arguments as such, even though they may *not* be
* printable data.
* Ptr -- pointer to some specific structure. Just print as hex for now.
* Stat -- a pointer to a stat buffer. Currently unused.
* Name -- pointer to a NULL-terminated string.
* BinString -- pointer to an array of chars, printed via strvisx().
* Ptr -- pointer to some unspecified structure. Just print as hex for now.
* Stat -- a pointer to a stat buffer. Prints a couple fields.
* Ioctl -- an ioctl command. Woefully limited.
* Quad -- a double-word value. e.g., lseek(int, offset_t, int)
* Signal -- a signal number. Prints the signal name (SIGxxx)
@ -17,10 +16,16 @@
* StringArray -- a pointer to an array of string pointers.
* Timespec -- a pointer to a struct timespec. Prints both elements.
* Timeval -- a pointer to a struct timeval. Prints both elements.
* Timeval2 -- a pointer to two struct timevals. Prints both elements of both.
* Itimerval -- a pointer to a struct itimerval. Prints all elements.
* Pollfd -- a pointer to an array of struct pollfd. Prints .fd and .events.
* Fd_set -- a pointer to an array of fd_set. Prints the fds that are set.
* Sigaction -- a pointer to a struct sigaction. Prints all elements.
* Umtx -- a pointer to a struct umtx. Prints the value of owner.
* Sigset -- a pointer to a sigset_t. Prints the signals that are set.
* Sigprocmask -- the first argument to sigprocmask(). Prints the name.
* Kevent -- a pointer to an array of struct kevents. Prints all elements.
* Pathconf -- the 2nd argument of patchconf().
*
* In addition, the pointer types (String, Ptr) may have OUT masked in --
* this means that the data is set on *return* from the system call -- or
@ -30,9 +35,12 @@
* $FreeBSD$
*/
enum Argtype { None = 1, Hex, Octal, Int, Name, String, Ptr, Stat, Ioctl, Quad,
Signal, Sockaddr, StringArray, Timespec, Timeval, Itimerval, Pollfd,
Fd_set, Sigaction, Fcntl, Mprot, Mmapflags, Whence, Readlinkres };
enum Argtype { None = 1, Hex, Octal, Int, Name, Ptr, Stat, Ioctl, Quad,
Signal, Sockaddr, StringArray, Timespec, Timeval, Itimerval, Pollfd,
Fd_set, Sigaction, Fcntl, Mprot, Mmapflags, Whence, Readlinkres,
Umtx, Sigset, Sigprocmask, Kevent, Sockdomain, Socktype, Open,
Fcntlflag, Rusage, BinString, Shutdown, Resource, Rlimit, Timeval2,
Pathconf };
#define ARG_MASK 0xff
#define OUT 0x100

View File

@ -46,6 +46,13 @@ static const char rcsid[] =
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioccom.h>
#include <machine/atomic.h>
#include <errno.h>
#include <sys/umtx.h>
#include <sys/event.h>
#include <sys/stat.h>
#include <sys/resource.h>
#include <ctype.h>
#include <err.h>
@ -58,18 +65,19 @@ static const char rcsid[] =
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <vis.h>
#include "truss.h"
#include "extern.h"
#include "syscall.h"
/*
* This should probably be in its own file.
* This should probably be in its own file, sorted alphabetically.
*/
struct syscall syscalls[] = {
{ "fcntl", 1, 3,
{ { Int, 0 } , { Fcntl, 1 }, { Hex, 2 }}},
{ { Int, 0 } , { Fcntl, 1 }, { Fcntlflag | OUT, 2 }}},
{ "readlink", 1, 3,
{ { Name, 0 } , { Readlinkres | OUT, 1 }, { Int, 2 }}},
{ "lseek", 2, 3,
@ -89,7 +97,7 @@ struct syscall syscalls[] = {
{ "mprotect", 1, 3,
{ { Ptr, 0 }, {Int, 1}, {Mprot, 2}}},
{ "open", 1, 3,
{ { Name | IN, 0} , { Hex, 1}, {Octal, 2}}},
{ { Name | IN, 0} , { Open, 1}, {Octal, 2}}},
{ "mkdir", 1, 2,
{ { Name, 0} , {Octal, 1}}},
{ "linux_open", 1, 3,
@ -115,17 +123,17 @@ struct syscall syscalls[] = {
{ "umount", 0, 2,
{ { Name, 0 }, { Int, 2 }}},
{ "fstat", 1, 2,
{ { Int, 0}, {Ptr | OUT , 1 }}},
{ { Int, 0}, { Stat | OUT , 1 }}},
{ "stat", 1, 2,
{ { Name | IN, 0 }, { Ptr | OUT, 1 }}},
{ { Name | IN, 0 }, { Stat | OUT, 1 }}},
{ "lstat", 1, 2,
{ { Name | IN, 0 }, { Ptr | OUT, 1 }}},
{ { Name | IN, 0 }, { Stat | OUT, 1 }}},
{ "linux_newstat", 1, 2,
{ { Name | IN, 0 }, { Ptr | OUT, 1 }}},
{ "linux_newfstat", 1, 2,
{ { Int, 0 }, { Ptr | OUT, 1 }}},
{ "write", 1, 3,
{ { Int, 0 }, { String | IN, 1 }, { Int, 2 }}},
{ { Int, 0 }, { BinString | IN, 1 }, { Int, 2 }}},
{ "ioctl", 1, 3,
{ { Int, 0 }, { Ioctl, 1 }, { Hex, 2 }}},
{ "break", 1, 1, { { Hex, 0 }}},
@ -134,19 +142,19 @@ struct syscall syscalls[] = {
{ "sigaction", 1, 3,
{ { Signal, 0 }, { Sigaction | IN, 1 }, { Sigaction | OUT, 2 }}},
{ "accept", 1, 3,
{ { Hex, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } },
{ { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } },
{ "bind", 1, 3,
{ { Hex, 0 }, { Sockaddr | IN, 1 }, { Int, 2 } } },
{ { Int, 0 }, { Sockaddr | IN, 1 }, { Int, 2 } } },
{ "connect", 1, 3,
{ { Hex, 0 }, { Sockaddr | IN, 1 }, { Int, 2 } } },
{ { Int, 0 }, { Sockaddr | IN, 1 }, { Int, 2 } } },
{ "getpeername", 1, 3,
{ { Hex, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } },
{ { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } },
{ "getsockname", 1, 3,
{ { Hex, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } },
{ { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } },
{ "recvfrom", 1, 6,
{ { Hex, 0 }, { Ptr | IN, 1 }, { Int, 3 }, { Hex, 3 }, { Sockaddr | OUT, 4 }, { Ptr | OUT, 5 } } },
{ { Int, 0 }, { BinString | OUT, 1 }, { Int, 2 }, { Hex, 3 }, { Sockaddr | OUT, 4 }, { Ptr | OUT, 5 } } },
{ "sendto", 1, 6,
{ { Hex, 0 }, { Ptr | IN, 1 }, { Int, 3 }, { Hex, 3 }, { Sockaddr | IN, 4 }, { Ptr | IN, 5 } } },
{ { Int, 0 }, { BinString | IN, 1 }, { Int, 2 }, { Hex, 3 }, { Sockaddr | IN, 4 }, { Ptr | IN, 5 } } },
{ "execve", 1, 3,
{ { Name | IN, 0 }, { StringArray | IN, 1 }, { StringArray | IN, 2 } } },
{ "linux_execve", 1, 3,
@ -161,24 +169,43 @@ struct syscall syscalls[] = {
{ "select", 1, 5, { { Int, 0 }, { Fd_set, 1 }, { Fd_set, 2 }, { Fd_set, 3 }, { Timeval, 4 }}},
{ "poll", 1, 3, { { Pollfd, 0 }, { Int, 1 }, { Int, 2 }}},
{ "gettimeofday", 1, 2, { { Timeval | OUT, 0 }, { Ptr, 1 }}},
{ "clock_gettime", 1, 2, { { Int, 0 }, { Timeval | OUT, 1 }}},
{ "recvfrom", 1, 6, { { Int, 0 }, { Ptr | OUT, 1 }, { Int, 2 }, { Int, 3 }, { Sockaddr | OUT, 4}, {Ptr | OUT, 5}}},
{ "clock_gettime", 1, 2, { { Int, 0 }, { Timespec | OUT, 1 }}},
{ "getitimer", 1, 2, { { Int, 0 }, { Itimerval | OUT, 2 }}},
{ "setitimer", 1, 3, { { Int, 0 }, { Itimerval, 1} , { Itimerval | OUT, 2 }}},
{ "kse_release", 0, 1, { { Timespec, 0 }}},
{ "kevent", 0, 6, { { Int, 0 }, { Kevent, 1 }, { Int, 2 }, { Kevent | OUT, 3 }, { Int, 4 }, { Timespec, 5 }}},
{ "_umtx_lock", 0, 1, { { Umtx, 0 }}},
{ "_umtx_unlock", 0, 1, { { Umtx, 0 }}},
{ "sigprocmask", 0, 3, { { Sigprocmask, 0 }, { Sigset, 1 }, { Sigset | OUT, 2 }}},
{ "unmount", 1, 2, { { Name, 0 }, { Int, 1 }}},
{ "socket", 1, 3, { { Sockdomain, 0}, { Socktype, 1}, {Int, 2 }}},
{ "getrusage", 1, 2, { { Int, 0 }, { Rusage | OUT, 1 }}},
{ "__getcwd", 1, 2, { { Name | OUT, 0}, { Int, 1 }}},
{ "shutdown", 1, 2, { { Int, 0}, { Shutdown, 1}}},
{ "getrlimit", 1, 2, { { Resource, 0}, {Rlimit | OUT, 1}}},
{ "setrlimit", 1, 2, { { Resource, 0}, {Rlimit | IN, 1}}},
{ "utimes", 1, 2,
{ { Name | IN, 0 }, { Timeval | IN, 1 }}},
{ { Name | IN, 0 }, { Timeval2 | IN, 1 }}},
{ "lutimes", 1, 2,
{ { Name | IN, 0 }, { Timeval | IN, 1 }}},
{ { Name | IN, 0 }, { Timeval2 | IN, 1 }}},
{ "futimes", 1, 2,
{ { Int, 0 }, { Timeval | IN, 1 }}},
{ "chflags", 1, 2,
{ { Name | IN, 0 }, { Hex, 1 }}},
{ "lchflags", 1, 2,
{ { Name | IN, 0 }, { Hex, 1 }}},
{ "pathconf", 1, 2,
{ { Name | IN, 0 }, { Pathconf, 1 }}},
{ "truncate", 1, 3,
{ { Name | IN, 0 }, { Int | IN, 1 }, { Quad | IN, 2 }}},
{ "ftruncate", 1, 3,
{ { Int | IN, 0 }, { Int | IN, 1 }, { Quad | IN, 2 }}},
{ "kill", 1, 2,
{ { Int | IN, 0 }, { Signal | IN, 1}}},
{ "munmap", 1, 2,
{ { Ptr, 0 }, { Int, 1 }}},
{ "read", 1, 3,
{ { Int, 0}, { String | OUT, 1}, { Int, 2}}},
{ { Int, 0}, { BinString | OUT, 1}, { Int, 2}}},
{ "rename", 1, 2,
{ { Name , 0} , { Name, 1}}},
{ "symlink", 1, 2,
@ -186,6 +213,175 @@ struct syscall syscalls[] = {
{ 0, 0, 0, { { 0, 0 }}},
};
/* Xlat idea taken from strace */
struct xlat {
int val;
char *str;
};
#define X(a) { a, #a },
#define XEND { 0, NULL }
static struct xlat kevent_filters[] = {
X(EVFILT_READ) X(EVFILT_WRITE) X(EVFILT_AIO) X(EVFILT_VNODE)
X(EVFILT_PROC) X(EVFILT_SIGNAL) X(EVFILT_TIMER)
X(EVFILT_NETDEV) X(EVFILT_FS) X(EVFILT_READ) XEND
};
static struct xlat kevent_flags[] = {
X(EV_ADD) X(EV_DELETE) X(EV_ENABLE) X(EV_DISABLE) X(EV_ONESHOT)
X(EV_CLEAR) X(EV_FLAG1) X(EV_ERROR) X(EV_EOF) XEND
};
struct xlat poll_flags[] = {
X(POLLSTANDARD) X(POLLIN) X(POLLPRI) X(POLLOUT) X(POLLERR)
X(POLLHUP) X(POLLNVAL) X(POLLRDNORM) X(POLLRDBAND)
X(POLLWRBAND) X(POLLINIGNEOF) XEND
};
static struct xlat mmap_flags[] = {
X(MAP_SHARED) X(MAP_PRIVATE) X(MAP_FIXED) X(MAP_RENAME)
X(MAP_NORESERVE) X(MAP_RESERVED0080) X(MAP_RESERVED0100)
X(MAP_HASSEMAPHORE) X(MAP_STACK) X(MAP_NOSYNC) X(MAP_ANON)
X(MAP_NOCORE) XEND
};
static struct xlat mprot_flags[] = {
X(PROT_NONE) X(PROT_READ) X(PROT_WRITE) X(PROT_EXEC) XEND
};
static struct xlat whence_arg[] = {
X(SEEK_SET) X(SEEK_CUR) X(SEEK_END) XEND
};
static struct xlat sigaction_flags[] = {
X(SA_ONSTACK) X(SA_RESTART) X(SA_RESETHAND) X(SA_NOCLDSTOP)
X(SA_NODEFER) X(SA_NOCLDWAIT) X(SA_SIGINFO) XEND
};
static struct xlat fcntl_arg[] = {
X(F_DUPFD) X(F_GETFD) X(F_SETFD) X(F_GETFL) X(F_SETFL)
X(F_GETOWN) X(F_SETOWN) X(F_GETLK) X(F_SETLK) X(F_SETLKW) XEND
};
static struct xlat fcntlfd_arg[] = {
X(FD_CLOEXEC) XEND
};
static struct xlat fcntlfl_arg[] = {
X(O_APPEND) X(O_ASYNC) X(O_FSYNC) X(O_NONBLOCK) X(O_NOFOLLOW)
X(O_DIRECT) XEND
};
static struct xlat sockdomain_arg[] = {
X(PF_UNSPEC) X(PF_LOCAL) X(PF_UNIX) X(PF_INET) X(PF_IMPLINK)
X(PF_PUP) X(PF_CHAOS) X(PF_NETBIOS) X(PF_ISO) X(PF_OSI)
X(PF_ECMA) X(PF_DATAKIT) X(PF_CCITT) X(PF_SNA) X(PF_DECnet)
X(PF_DLI) X(PF_LAT) X(PF_HYLINK) X(PF_APPLETALK) X(PF_ROUTE)
X(PF_LINK) X(PF_XTP) X(PF_COIP) X(PF_CNT) X(PF_SIP) X(PF_IPX)
X(PF_RTIP) X(PF_PIP) X(PF_ISDN) X(PF_KEY) X(PF_INET6)
X(PF_NATM) X(PF_ATM) X(PF_NETGRAPH) X(PF_SLOW) X(PF_SCLUSTER)
X(PF_ARP) X(PF_BLUETOOTH) XEND
};
static struct xlat socktype_arg[] = {
X(SOCK_STREAM) X(SOCK_DGRAM) X(SOCK_RAW) X(SOCK_RDM)
X(SOCK_SEQPACKET) XEND
};
static struct xlat open_flags[] = {
X(O_RDONLY) X(O_WRONLY) X(O_RDWR) X(O_ACCMODE) X(O_NONBLOCK)
X(O_APPEND) X(O_SHLOCK) X(O_EXLOCK) X(O_ASYNC) X(O_FSYNC)
X(O_NOFOLLOW) X(O_CREAT) X(O_TRUNC) X(O_EXCL) X(O_NOCTTY)
X(O_DIRECT) XEND
};
static struct xlat shutdown_arg[] = {
X(SHUT_RD) X(SHUT_WR) X(SHUT_RDWR) XEND
};
static struct xlat resource_arg[] = {
X(RLIMIT_CPU) X(RLIMIT_FSIZE) X(RLIMIT_DATA) X(RLIMIT_STACK)
X(RLIMIT_CORE) X(RLIMIT_RSS) X(RLIMIT_MEMLOCK) X(RLIMIT_NPROC)
X(RLIMIT_NOFILE) X(RLIMIT_SBSIZE) X(RLIMIT_VMEM) XEND
};
static struct xlat pathconf_arg[] = {
X(_PC_LINK_MAX) X(_PC_MAX_CANON) X(_PC_MAX_INPUT)
X(_PC_NAME_MAX) X(_PC_PATH_MAX) X(_PC_PIPE_BUF)
X(_PC_CHOWN_RESTRICTED) X(_PC_NO_TRUNC) X(_PC_VDISABLE)
X(_PC_ASYNC_IO) X(_PC_PRIO_IO) X(_PC_SYNC_IO)
X(_PC_ALLOC_SIZE_MIN) X(_PC_FILESIZEBITS)
X(_PC_REC_INCR_XFER_SIZE) X(_PC_REC_MAX_XFER_SIZE)
X(_PC_REC_MIN_XFER_SIZE) X(_PC_REC_XFER_ALIGN)
X(_PC_SYMLINK_MAX) X(_PC_ACL_EXTENDED) X(_PC_ACL_PATH_MAX)
X(_PC_CAP_PRESENT) X(_PC_INF_PRESENT) X(_PC_MAC_PRESENT)
XEND
};
#undef X
#undef XEND
/* Searches an xlat array for a value, and returns it if found. Otherwise
return a string representation. */
char *lookup(struct xlat *xlat, int val, int base)
{
static char tmp[16];
for (; xlat->str != NULL; xlat++)
if (xlat->val == val)
return xlat->str;
switch (base) {
case 8:
sprintf(tmp, "0%o", val);
break;
case 16:
sprintf(tmp, "0x%x", val);
break;
case 10:
sprintf(tmp, "%u", val);
break;
default:
errx(1,"Unknown lookup base");
break;
}
return tmp;
}
char *xlookup(struct xlat *xlat, int val)
{
return lookup(xlat, val, 16);
}
/* Searches an xlat array containing bitfield values. Remaining bits
set after removing the known ones are printed at the end:
IN|0x400 */
char *xlookup_bits(struct xlat *xlat, int val)
{
static char str[512];
int len = 0;
int rem = val;
for (; xlat->str != NULL; xlat++)
{
if ((xlat->val & rem) == xlat->val)
{
/* don't print the "all-bits-zero" string unless all
bits are really zero */
if (xlat->val == 0 && val != 0)
continue;
len += sprintf(str + len, "%s|", xlat->str);
rem &= ~(xlat->val);
}
}
/* if we have leftover bits or didn't match anything */
if (rem || len == 0)
len += sprintf(str + len, "0x%x", rem);
if (len && str[len - 1] == '|')
len--;
str[len] = 0;
return str;
}
/*
* If/when the list gets big, it might be desirable to do it
* as a hash table or binary search.
@ -262,19 +458,6 @@ get_string(int procfd, void *offset, int max) {
}
/*
* Remove a trailing '|' in a string, useful for fixup after decoding
* a "flags" argument.
*/
void
remove_trailing_or(char *str)
{
if (str != NULL && (str = rindex(str, '|')) != NULL && str[1] == '\0')
*str = '\0';
}
/*
* print_arg
* Converts a syscall argument into a string. Said string is
@ -288,7 +471,6 @@ remove_trailing_or(char *str)
char *
print_arg(int fd, struct syscall_args *sc, unsigned long *args, long retval, struct trussinfo *trussinfo) {
char *tmp = NULL;
int max = 0;
switch (sc->type & ARG_MASK) {
case Hex:
@ -300,24 +482,52 @@ print_arg(int fd, struct syscall_args *sc, unsigned long *args, long retval, str
case Int:
asprintf(&tmp, "%ld", args[sc->offset]);
break;
case String:
max = trussinfo->strsize;
if (max == 0)
{
asprintf(&tmp, "0x%lx", args[sc->offset]);
break;
}
case Name:
{
/* NULL-terminated string. */
char *tmp2;
tmp2 = get_string(fd, (void*)args[sc->offset], max ? max + 1 : 0);
if (max && memchr(tmp2, '\0', max + 1) == NULL)
asprintf(&tmp, "\"%.*s...\"", max, tmp2);
else
asprintf(&tmp, "\"%s\"", tmp2);
tmp2 = get_string(fd, (void*)args[sc->offset], 0);
asprintf(&tmp, "\"%s\"", tmp2);
free(tmp2);
}
break;
case BinString:
{
/* Binary block of data that might have printable characters.
XXX If type|OUT, assume that the length is the syscall's
return value. Otherwise, assume that the length of the block
is in the next syscall argument. */
int max_string = trussinfo->strsize;
char tmp2[max_string+1], *tmp3;
int len;
int truncated = 0;
if (sc->type & OUT)
len = retval;
else
len = args[sc->offset + 1];
/* Don't print more than max_string characters, to avoid word
wrap. If we have to truncate put some ... after the string.
*/
if (len > max_string) {
len = max_string;
truncated = 1;
}
if (len && get_struct(fd, (void*)args[sc->offset], &tmp2, len) != -1) {
tmp3 = malloc(len * 4 + 1);
while (len) {
if (strvisx(tmp3, tmp2, len, VIS_CSTYLE|VIS_TAB|VIS_NL) <= max_string)
break;
len--;
truncated = 1;
};
asprintf(&tmp, "\"%s\"%s", tmp3, truncated?"...":"");
free(tmp3);
} else
asprintf(&tmp, "0x%lx", args[sc->offset]);
}
break;
case StringArray:
{
int num, size, i;
@ -384,7 +594,22 @@ print_arg(int fd, struct syscall_args *sc, unsigned long *args, long retval, str
{
const char *temp = ioctlname(args[sc->offset]);
if (temp)
tmp = strdup(temp);
tmp = strdup(temp);
else
{
unsigned long arg = args[sc->offset];
asprintf(&tmp, "0x%lx { IO%s%s 0x%lx('%c'), %lu, %lu}", arg,
arg&IOC_OUT?"R":"", arg&IOC_IN?"W":"",
IOCGROUP(arg), isprint(IOCGROUP(arg))?(char)IOCGROUP(arg):'?',
arg & 0xFF, IOCPARM_LEN(arg));
}
}
break;
case Umtx:
{
struct umtx umtx;
if (get_struct(fd, (void *)args[sc->offset], &umtx, sizeof(umtx)) != -1)
asprintf(&tmp, "{0x%lx}", (long)umtx.u_owner);
else
asprintf(&tmp, "0x%lx", args[sc->offset]);
}
@ -393,7 +618,7 @@ print_arg(int fd, struct syscall_args *sc, unsigned long *args, long retval, str
{
struct timespec ts;
if (get_struct(fd, (void *)args[sc->offset], &ts, sizeof(ts)) != -1)
asprintf(&tmp, "{%jd %jd}", (intmax_t)ts.tv_sec, (intmax_t)ts.tv_nsec);
asprintf(&tmp, "{%ld.%09ld}", (long)ts.tv_sec, ts.tv_nsec);
else
asprintf(&tmp, "0x%lx", args[sc->offset]);
}
@ -402,7 +627,18 @@ print_arg(int fd, struct syscall_args *sc, unsigned long *args, long retval, str
{
struct timeval tv;
if (get_struct(fd, (void *)args[sc->offset], &tv, sizeof(tv)) != -1)
asprintf(&tmp, "{%jd %jd}", (intmax_t)tv.tv_sec, (intmax_t)tv.tv_usec);
asprintf(&tmp, "{%ld.%06ld}", (long)tv.tv_sec, tv.tv_usec);
else
asprintf(&tmp, "0x%lx", args[sc->offset]);
}
break;
case Timeval2:
{
struct timeval tv[2];
if (get_struct(fd, (void *)args[sc->offset], &tv, sizeof(tv)) != -1)
asprintf(&tmp, "{%ld.%06ld, %ld.%06ld}",
(long)tv[0].tv_sec, tv[0].tv_usec,
(long)tv[1].tv_sec, tv[1].tv_usec);
else
asprintf(&tmp, "0x%lx", args[sc->offset]);
}
@ -411,11 +647,11 @@ print_arg(int fd, struct syscall_args *sc, unsigned long *args, long retval, str
{
struct itimerval itv;
if (get_struct(fd, (void *)args[sc->offset], &itv, sizeof(itv)) != -1)
asprintf(&tmp, "{%jd %jd, %jd %jd}",
(intmax_t)itv.it_interval.tv_sec,
(intmax_t)itv.it_interval.tv_usec,
(intmax_t)itv.it_value.tv_sec,
(intmax_t)itv.it_value.tv_usec);
asprintf(&tmp, "{%ld.%06ld, %ld.%06ld}",
(long)itv.it_interval.tv_sec,
itv.it_interval.tv_usec,
(long)itv.it_value.tv_sec,
itv.it_value.tv_usec);
else
asprintf(&tmp, "0x%lx", args[sc->offset]);
}
@ -443,24 +679,12 @@ print_arg(int fd, struct syscall_args *sc, unsigned long *args, long retval, str
tmp[used++] = '{';
for (i = 0; i < numfds; i++) {
#define POLLKNOWN_EVENTS \
(POLLIN | POLLPRI | POLLOUT | POLLERR | POLLHUP | POLLNVAL | \
POLLRDNORM |POLLRDBAND | POLLWRBAND | POLLINIGNEOF)
u = snprintf(tmp + used, per_fd,
"%s%d 0x%hx%s%s%s%s%s%s%s%s%s ",
"%s%d/%s",
i > 0 ? " " : "",
pfd[i].fd,
pfd[i].events & ~POLLKNOWN_EVENTS,
pfd[i].events & POLLIN ? "" : "|IN",
pfd[i].events & POLLPRI ? "" : "|PRI",
pfd[i].events & POLLOUT ? "" : "|OUT",
pfd[i].events & POLLERR ? "" : "|ERR",
pfd[i].events & POLLHUP ? "" : "|HUP",
pfd[i].events & POLLNVAL ? "" : "|NVAL",
pfd[i].events & POLLRDNORM ? "" : "|RDNORM",
pfd[i].events & POLLRDBAND ? "" : "|RDBAND",
pfd[i].events & POLLWRBAND ? "" : "|WRBAND");
xlookup_bits(poll_flags, pfd[i].events) );
if (u > 0)
used += u < per_fd ? u : per_fd;
}
@ -518,63 +742,98 @@ print_arg(int fd, struct syscall_args *sc, unsigned long *args, long retval, str
asprintf(&tmp, "%ld", sig);
}
break;
case Fcntl:
case Sigset:
{
long sig;
sigset_t ss;
int i, used;
sig = args[sc->offset];
if (get_struct(fd, (void *)args[sc->offset], (void *)&ss,
sizeof(ss)) == -1)
{
asprintf(&tmp, "0x%lx", args[sc->offset]);
break;
}
tmp = malloc(sys_nsig * 8); /* 7 bytes avg per signal name */
used = 0;
for (i = 1; i < sys_nsig; i++)
{
if (sigismember(&ss, i))
{
used += sprintf(tmp + used, "%s|", strsig(i));
}
}
if(used)
tmp[used-1] = 0;
else
strcpy(tmp, "0x0");
}
break;
case Sigprocmask:
{
switch (args[sc->offset]) {
#define S(a) case a: tmp = strdup(#a); break;
S(F_DUPFD);
S(F_GETFD);
S(F_SETFD);
S(F_GETFL);
S(F_SETFL);
S(F_GETOWN);
S(F_SETOWN);
S(F_GETLK);
S(F_SETLK);
S(F_SETLKW);
S(SIG_BLOCK);
S(SIG_UNBLOCK);
S(SIG_SETMASK);
#undef S
}
if (tmp == NULL)
asprintf(&tmp, "0x%lx", args[sc->offset]);
}
break;
case Fcntlflag:
{
/* XXX output depends on the value of the previous argument */
switch (args[sc->offset-1]) {
case F_SETFD:
tmp = strdup(xlookup_bits(fcntlfd_arg, args[sc->offset]));
break;
case F_SETFL:
tmp = strdup(xlookup_bits(fcntlfl_arg, args[sc->offset]));
break;
case F_GETFD:
case F_GETFL:
case F_GETOWN:
tmp = strdup("");
break;
default:
asprintf(&tmp, "0x%lx", args[sc->offset]);
break;
}
}
break;
case Open:
tmp = strdup(xlookup_bits(open_flags, args[sc->offset]));
break;
case Fcntl:
tmp = strdup(xlookup(fcntl_arg, args[sc->offset]));
break;
case Mprot:
{
#define S(a) ((args[sc->offset] & a) ? #a "|" : "")
asprintf(&tmp, "(0x%lx)%s%s%s%s", args[sc->offset],
S(PROT_NONE), S(PROT_READ), S(PROT_WRITE), S(PROT_EXEC));
#undef S
remove_trailing_or(tmp);
}
tmp = strdup(xlookup_bits(mprot_flags, args[sc->offset]));
break;
case Mmapflags:
{
#define S(a) ((args[sc->offset] & a) ? #a "|" : "")
asprintf(&tmp, "(0x%lx)%s%s%s%s%s%s%s%s", args[sc->offset],
S(MAP_ANON), S(MAP_FIXED), S(MAP_HASSEMAPHORE),
S(MAP_NOCORE), S(MAP_NOSYNC), S(MAP_PRIVATE),
S(MAP_SHARED), S(MAP_STACK));
#undef S
remove_trailing_or(tmp);
}
tmp = strdup(xlookup_bits(mmap_flags, args[sc->offset]));
break;
case Whence:
{
switch (args[sc->offset]) {
#define S(a) case a: tmp = strdup(#a); break;
S(SEEK_SET);
S(SEEK_CUR);
S(SEEK_END);
#undef S
default: asprintf(&tmp, "0x%lx", args[sc->offset]); break;
}
}
tmp = strdup(xlookup(whence_arg, args[sc->offset]));
break;
case Sockdomain:
tmp = strdup(xlookup(sockdomain_arg, args[sc->offset]));
break;
case Socktype:
tmp = strdup(xlookup(socktype_arg, args[sc->offset]));
break;
case Shutdown:
tmp = strdup(xlookup(shutdown_arg, args[sc->offset]));
break;
case Resource:
tmp = strdup(xlookup(resource_arg, args[sc->offset]));
break;
case Pathconf:
tmp = strdup(xlookup(pathconf_arg, args[sc->offset]));
break;
case Sockaddr:
{
@ -653,10 +912,6 @@ print_arg(int fd, struct syscall_args *sc, unsigned long *args, long retval, str
struct sigaction sa;
char *hand;
const char *h;
#define SA_KNOWN_FLAGS \
(SA_ONSTACK | SA_RESTART | SA_RESETHAND | SA_NOCLDSTOP | SA_NODEFER | \
SA_NOCLDWAIT | SA_SIGINFO)
if (get_struct(fd, (void *)args[sc->offset], &sa, sizeof(sa)) != -1) {
@ -667,35 +922,108 @@ print_arg(int fd, struct syscall_args *sc, unsigned long *args, long retval, str
h = "SIG_IGN";
else
h = hand;
asprintf(&tmp, "{ %s 0x%x%s%s%s%s%s%s%s ss_t }",
asprintf(&tmp, "{ %s %s ss_t }",
h,
sa.sa_flags & ~SA_KNOWN_FLAGS,
sa.sa_flags & SA_ONSTACK ? "" : "|ONSTACK",
sa.sa_flags & SA_RESTART ? "" : "|RESTART",
sa.sa_flags & SA_RESETHAND ? "" : "|RESETHAND",
sa.sa_flags & SA_NOCLDSTOP ? "" : "|NOCLDSTOP",
sa.sa_flags & SA_NODEFER ? "" : "|NODEFER",
sa.sa_flags & SA_NOCLDWAIT ? "" : "|NOCLDWAIT",
sa.sa_flags & SA_SIGINFO ? "" : "|SIGINFO");
xlookup_bits(sigaction_flags, sa.sa_flags));
free(hand);
} else
asprintf(&tmp, "0x%lx", args[sc->offset]);
}
break;
case Kevent:
{
/*
* XXX XXX: the size of the array is determined by either the
* next syscall argument, or by the syscall returnvalue,
* depending on which argument number we are. This matches the
* kevent syscall, but luckily that's the only syscall that uses
* them.
*/
struct kevent *ke;
int numevents = -1;
int bytes = 0;
int i, tmpsize, u, used;
const int per_ke = 100;
if (sc->offset == 1)
numevents = args[sc->offset+1];
else if (sc->offset == 3 && retval != -1)
numevents = retval;
if (numevents >= 0)
bytes = sizeof(struct kevent) * numevents;
if ((ke = malloc(bytes)) == NULL)
err(1, "Cannot malloc %d bytes for kevent array", bytes);
if (numevents >= 0 && get_struct(fd, (void *)args[sc->offset], ke, bytes) != -1) {
used = 0;
tmpsize = 1 + per_ke * numevents + 2;
if ((tmp = malloc(tmpsize)) == NULL)
err(1, "Cannot alloc %d bytes for kevent output", tmpsize);
tmp[used++] = '{';
for (i = 0; i < numevents; i++) {
u = snprintf(tmp + used, per_ke,
"%s%p,%s,%s,%d,%p,%p",
i > 0 ? " " : "",
(void *)ke[i].ident,
xlookup(kevent_filters, ke[i].filter),
xlookup_bits(kevent_flags, ke[i].flags),
ke[i].fflags,
(void *)ke[i].data,
(void *)ke[i].udata);
if (u > 0)
used += u < per_ke ? u : per_ke;
}
tmp[used++] = '}';
tmp[used++] = '\0';
} else
asprintf(&tmp, "0x%lx", args[sc->offset]);
free(ke);
}
break;
case Stat:
{
struct stat st;
if (get_struct(fd, (void *)args[sc->offset], &st, sizeof(st)) != -1) {
char mode[12];
strmode(st.st_mode, mode);
asprintf(&tmp, "{mode=%s,inode=%jd,size=%jd,blksize=%ld}",
mode,
(intmax_t)st.st_ino,(intmax_t)st.st_size,(long)st.st_blksize);
} else
asprintf(&tmp, "0x%lx", args[sc->offset]);
}
break;
case Rusage:
{
struct rusage ru;
if (get_struct(fd, (void *)args[sc->offset], &ru, sizeof(ru)) != -1)
asprintf(&tmp, "{u=%ld.%06ld,s=%ld.%06ld,in=%ld,out=%ld}",
(long)ru.ru_utime.tv_sec, ru.ru_utime.tv_usec,
(long)ru.ru_stime.tv_sec, ru.ru_stime.tv_usec,
ru.ru_inblock, ru.ru_oublock);
else
asprintf(&tmp, "0x%lx", args[sc->offset]);
}
break;
case Rlimit:
{
struct rlimit rl;
if (get_struct(fd, (void *)args[sc->offset], &rl, sizeof(rl)) != -1)
asprintf(&tmp, "{cur=%ju,max=%ju}",
rl.rlim_cur, rl.rlim_max);
else
asprintf(&tmp, "0x%lx", args[sc->offset]);
}
break;
default:
errx(1, "Invalid argument type %d\n", sc->type & ARG_MASK);
}
return tmp;
}
#define timespecsubt(tvp, uvp, vvp) \
do { \
(vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \
(vvp)->tv_nsec = (tvp)->tv_nsec - (uvp)->tv_nsec; \
if ((vvp)->tv_nsec < 0) { \
(vvp)->tv_sec--; \
(vvp)->tv_nsec += 1000000000; \
} \
} while (0)
/*
* print_syscall

View File

@ -44,3 +44,13 @@ struct trussinfo
struct timespec before;
struct timespec after;
};
#define timespecsubt(tvp, uvp, vvp) \
do { \
(vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \
(vvp)->tv_nsec = (tvp)->tv_nsec - (uvp)->tv_nsec; \
if ((vvp)->tv_nsec < 0) { \
(vvp)->tv_sec--; \
(vvp)->tv_nsec += 1000000000; \
} \
} while (0)