summaryrefslogtreecommitdiffstats
path: root/usr.bin/truss
diff options
context:
space:
mode:
authorpav <pav@FreeBSD.org>2006-05-15 21:18:28 +0000
committerpav <pav@FreeBSD.org>2006-05-15 21:18:28 +0000
commita233d0a2ec889dcec576eb60c9c97703c6e6411d (patch)
treebc8d8d905841a0fb10ed22ce3afee50b7fefe6a5 /usr.bin/truss
parenta95ed1e45aefd859e733662f5150c4d8d1ece4c8 (diff)
downloadFreeBSD-src-a233d0a2ec889dcec576eb60c9c97703c6e6411d.zip
FreeBSD-src-a233d0a2ec889dcec576eb60c9c97703c6e6411d.tar.gz
- 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
Diffstat (limited to 'usr.bin/truss')
-rw-r--r--usr.bin/truss/amd64-fbsd32.c18
-rw-r--r--usr.bin/truss/i386-fbsd.c18
-rw-r--r--usr.bin/truss/main.c34
-rw-r--r--usr.bin/truss/setup.c37
-rw-r--r--usr.bin/truss/syscall.h24
-rw-r--r--usr.bin/truss/syscalls.c590
-rw-r--r--usr.bin/truss/truss.h10
7 files changed, 578 insertions, 153 deletions
diff --git a/usr.bin/truss/amd64-fbsd32.c b/usr.bin/truss/amd64-fbsd32.c
index d9ee202..ed98a5b 100644
--- a/usr.bin/truss/amd64-fbsd32.c
+++ b/usr.bin/truss/amd64-fbsd32.c
@@ -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)
@@ -329,6 +330,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.
*/
diff --git a/usr.bin/truss/i386-fbsd.c b/usr.bin/truss/i386-fbsd.c
index d9ee202..ed98a5b 100644
--- a/usr.bin/truss/i386-fbsd.c
+++ b/usr.bin/truss/i386-fbsd.c
@@ -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)
@@ -329,6 +330,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.
*/
diff --git a/usr.bin/truss/main.c b/usr.bin/truss/main.c
index 34c4e7a..2096442 100644
--- a/usr.bin/truss/main.c
+++ b/usr.bin/truss/main.c
@@ -279,6 +279,7 @@ START_TRACE:
do {
int val = 0;
+ struct timespec timediff;
if (ioctl(Procfd, PIOCWAIT, &pfs) == -1)
warn("PIOCWAIT top of loop");
@@ -328,6 +329,23 @@ START_TRACE:
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 @@ START_TRACE:
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;
diff --git a/usr.bin/truss/setup.c b/usr.bin/truss/setup.c
index c48370c..a6f8864 100644
--- a/usr.bin/truss/setup.c
+++ b/usr.bin/truss/setup.c
@@ -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) {
diff --git a/usr.bin/truss/syscall.h b/usr.bin/truss/syscall.h
index 2783486..e7f6c3f 100644
--- a/usr.bin/truss/syscall.h
+++ b/usr.bin/truss/syscall.h
@@ -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
diff --git a/usr.bin/truss/syscalls.c b/usr.bin/truss/syscalls.c
index b94df2d..33a1c0b 100644
--- a/usr.bin/truss/syscalls.c
+++ b/usr.bin/truss/syscalls.c
@@ -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.
@@ -263,19 +459,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
* allocated via malloc(), so needs to be free()'d. The file
@@ -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 Mprot:
+
+ case Fcntlflag:
{
-
-#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);
-
+ /* 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:
+ 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
diff --git a/usr.bin/truss/truss.h b/usr.bin/truss/truss.h
index 24558ba..575260b 100644
--- a/usr.bin/truss/truss.h
+++ b/usr.bin/truss/truss.h
@@ -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)
OpenPOWER on IntegriCloud