Index: setup.c
===================================================================
--- setup.c	(revision 228100)
+++ setup.c	(working copy)
@@ -56,9 +56,15 @@
 
 #include "truss.h"
 #include "extern.h"
+#include "syscall.h"
 
 static int child_pid;
 
+/* Are we waiting for the child to trap, handling a syscall/signal, or
+   trying to exit?  Note that this is our status, not the child's */
+static enum { TS_WAITING, TS_RUNNING, TS_EXITING } 
+	truss_state = TS_WAITING;
+
 /*
  * setup_and_wait() is called to start a process.  All it really does
  * is fork(), set itself up to stop on exec or exit, and then exec
@@ -104,18 +110,21 @@
 {
 	int waitval;
 	int ret;
-	int retry = 10;
+	int retry = 0;
 
+	/* If we just forked a process, it may not be created immediately.
+	   Retry for up to 2 seconds.  */
 	do {
+		if (retry)
+			usleep(200);
 		ret = ptrace(PT_ATTACH, pid, NULL, 0);
-		usleep(200);
-	} while(ret && retry-- > 0);
+	} while(ret && retry++ < 10);
 	if (ret)
 		err(1, "can not attach to target process");
 
 	child_pid = pid;	
 	if (waitpid(pid, &waitval, 0) < 0) 
-		err(1, "Unexpect stop in waitpid");
+		err(1, "Unexpected stop in waitpid");
 
 	return (0);
 }
@@ -131,6 +140,15 @@
 {
 	int waitval;
 
+	/* If we are currently processing a syscall, we can't detach from
+	   the child.  Return and we will exit after we have finished
+	   with the current syscall */
+	if (truss_state == TS_RUNNING)
+	{
+		truss_state = TS_EXITING;
+		return;
+	}
+
 	/* stop the child so that we can detach */	
 	kill(child_pid, SIGSTOP);
 	if (waitpid(child_pid, &waitval, 0) < 0)
@@ -165,27 +183,70 @@
 	np->tid = lwpid;
 	np->in_fork = 0;
 	np->in_syscall = 0;
+	np->threadnum = info->threadcount;
+	np->fsc = NULL;
 	SLIST_INSERT_HEAD(&info->threadlist, np, entries);
 	info->curthread = np;
+	info->threadcount++;
 }
 
 /*
- * Start the traced process and wait until it stoped.
+ * Start the traced process and wait until it has stopped.
  * Fill trussinfo structure.
- * When this even returns, the traced process is in stop state.
+ * When this event returns, the traced process is in stop state.
  */
 void
 waitevent(struct trussinfo *info)
 {
 	int waitval;
+	int rv;
 	static int pending_signal = 0;
 	
-	ptrace(PT_SYSCALL, info->pid, (caddr_t)1, pending_signal);
+	if (truss_state == TS_RUNNING)
+		truss_state = TS_WAITING;
+
+	rv = ptrace(PT_SYSCALL, info->pid, (caddr_t)1, pending_signal);
+	if (rv == -1) {
+		if (errno == ESRCH)
+			exit(0);
+	    err(1, "ptrace error");
+	}
+
+	if (truss_state == TS_EXITING)
+		restore_proc(0);
+
+	truss_state = TS_RUNNING;
+  	if (info->curthread)
+  		info->curthread->flags &= ~(ENTRY_ONLY|PRINTED_ENTRY|EXIT_ONLY);
+	
 	pending_signal = 0;
 
-	if (waitpid(info->pid, &waitval, 0) < 0) {
-		err(1, "Unexpected stop in waitpid");
+ AGAIN:
+	if (info->curthread && info->curthread->in_syscall &&
+        (info->curthread->flags & PRINTED_ENTRY) == 0)
+    {
+		alarm(2);
 	}
+	rv = waitpid(info->pid, &waitval, 0);
+	if (rv == -1) 
+	{
+		if (errno == EINTR)
+		{
+			/* The 2-second timer interrupted us, so we must be 
+			    waiting on a slow syscall.  Print its name and
+			    continue waiting, this time forever. */
+			info->curthread->flags |= ENTRY_ONLY;
+			print_syscall(info, info->curthread->syscallname, 1, NULL);
+			fprintf(info->outfile, "...sleeping)\n");
+			fflush(info->outfile);
+			alarm(0);
+			goto AGAIN;
+		} else
+			err(1, "Unexpected stop in waitpid");
+	}
+
+	if (info->curthread && info->curthread->in_syscall)
+		alarm(0);
 	
 	if (WIFCONTINUED(waitval)) {
 		info->pr_why = S_NONE;
@@ -199,11 +260,18 @@
 	if (WIFSTOPPED(waitval)) {
 		struct ptrace_lwpinfo lwpinfo;
 		ptrace(PT_LWPINFO, info->pid, (caddr_t)&lwpinfo, sizeof(lwpinfo));	
+		/* fprintf(info->outfile,"[%d/%d]", lwpinfo.pl_lwpid,lwpinfo.pl_event);
+		fflush(info->outfile); */
 		find_thread(info, lwpinfo.pl_lwpid);
 		switch(WSTOPSIG(waitval)) {
 		case SIGTRAP:
-			info->pr_why = info->curthread->in_syscall?S_SCX:S_SCE;
-			info->curthread->in_syscall = 1 - info->curthread->in_syscall;
+			if ((lwpinfo.pl_flags & (PL_FLAG_SCE|PL_FLAG_SCX))==0)
+				err(1,"pl_flags=%x contains neither PL_FLAG_SCE or PL_FLAG_SCX",lwpinfo.pl_flags);
+			info->pr_why = (lwpinfo.pl_flags&PL_FLAG_SCE)?S_SCE:S_SCX;
+			info->curthread->in_syscall = info->pr_why==S_SCE?1:0;
+			
+			/* fprintf(info->outfile,"<pl_flags=%d in_syscall=%d>\n",lwpinfo.pl_flags,info->curthread->in_syscall);
+			fflush(info->outfile); */
 			break;
 		default:
 			info->pr_why = S_SIG;
@@ -214,7 +282,7 @@
 	}
 	if (WIFSIGNALED(waitval)) {
 		info->pr_why = S_EXIT;
-		info->pr_data = 0;
+		info->pr_data = WTERMSIG(waitval)+128;
 		return;
 	}
 }
Index: syscall.h
===================================================================
--- syscall.h	(revision 228100)
+++ syscall.h	(working copy)
@@ -26,6 +26,20 @@
  * 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 pathconf().
+ * Umtxop -- the 2nd argument to _umtx_op().
+ * Msgflags -- the flags argument of recvmsg, sendmsg and friends.
+ * Int32Ptr -- pointer to an int32.
+ * Ptrace -- the 1st argument to ptrace().
+ * WaitStat -- the "int *status" argument to waitpid, wait3, wait4.
+ * WaitOpts -- the "int options" argument to waitpid, wait4.
+ * Clockid -- clockid_t type; 1st argument to clock_gettime().
+ * Chflags -- the 2nd argument to chflags().
+ * Sendfilehdtr -- the 5th argument to sendfile().
+ * Sendfileflags -- the 7th argument to sendfile().
+ * Madvise -- the 3rd argument to madvise().
+ * UmtxArg1, UmtxArg4, UmtxArg5 -- _umtx_op is heavily overloaded, and the
+ *   types of these arguments depends on the value of Umtxop
+ * Utrace -- the 1st argument to utrace().
  *
  * 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
@@ -37,10 +51,12 @@
 
 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,
+	Fd_set, Sigaction, Fcntl, Mprot, Mmapflags, Whence,
 	Umtx, Sigset, Sigprocmask, Kevent, Sockdomain, Socktype, Open,
 	Fcntlflag, Rusage, BinString, Shutdown, Resource, Rlimit, Timeval2,
-	Pathconf };
+	Iovec, Pathconf, Umtxop, Msgflags, Int32Ptr, Ptrace, Waitstat,
+	Waitopts, Clockid, Chflags, Sendfilehdtr, Sendfileflags, Madvise,
+	UmtxArg1, UmtxArg4, UmtxArg5, Utrace };
 
 #define ARG_MASK	0xff
 #define OUT	0x100
Index: Makefile
===================================================================
--- Makefile	(revision 228100)
+++ Makefile	(working copy)
@@ -5,6 +5,13 @@
 PROG=	truss
 SRCS=	main.c setup.c syscalls.c syscalls.h ioctl.c ${MACHINE_ARCH}-fbsd.c
 
+LDADD=	-lsbuf
+
+DEBUG_FLAGS=	-g
+
+#CC=ccache gcc
+#CC=ccache gcc34 -fbounds-checking -static
+
 CFLAGS+= -I${.CURDIR} -I.
 CLEANFILES= syscalls.master syscalls.h ioctl.c
 
Index: syscalls.c
===================================================================
--- syscalls.c	(revision 228100)
+++ syscalls.c	(working copy)
@@ -43,6 +43,7 @@
 #include <sys/types.h>
 #include <sys/ptrace.h>
 #include <sys/socket.h>
+#include <sys/sbuf.h>
 #include <sys/time.h>
 #include <sys/un.h>
 #include <netinet/in.h>
@@ -54,6 +55,8 @@
 #include <sys/event.h>
 #include <sys/stat.h>
 #include <sys/resource.h>
+#include <sys/ptrace.h>
+#include <sys/wait.h>
 
 #include <ctype.h>
 #include <err.h>
@@ -104,8 +107,10 @@
 	{ .name = "getsid", .ret_type = 1, .nargs = 1,
 	  .args = { { Int, 0 } } },
 	{ .name = "getuid", .ret_type = 1, .nargs = 0 },
+	{ .name = "issetugid", .ret_type = 1, .nargs = 0 },
+	{ .name = "kqueue", .ret_type = 1, .nargs = 0 },
 	{ .name = "readlink", .ret_type = 1, .nargs = 3,
-	  .args = { { Name, 0 } , { Readlinkres | OUT, 1 }, { Int, 2 } } },
+	  .args = { { Name, 0 } , { BinString | OUT, 1 }, { Int, 2 } } },
 	{ .name = "lseek", .ret_type = 2, .nargs = 3,
 	  .args = { { Int, 0 }, { Quad, 1 + QUAD_ALIGN }, { Whence, 1 + QUAD_SLOTS + QUAD_ALIGN } } },
 	{ .name = "linux_lseek", .ret_type = 2, .nargs = 3,
@@ -128,6 +133,8 @@
 	  .args = { { Name, 0 } } },
 	{ .name = "chdir", .ret_type = 0, .nargs = 1,
 	  .args = { { Name, 0 } } },
+	{ .name = "fchdir", .ret_type = 0, .nargs = 1,
+	  .args = { { Int, 0 } } },
 	{ .name = "chroot", .ret_type = 0, .nargs = 1,
 	  .args = { { Name, 0 } } },
 	{ .name = "mknod", .ret_type = 0, .nargs = 3,
@@ -152,6 +159,10 @@
 	  .args = { { Int, 0 }, { Ptr | OUT, 1 } } },
 	{ .name = "write", .ret_type = 1, .nargs = 3,
 	  .args = { { Int, 0 }, { BinString | IN, 1 }, { Int, 2 } } },
+	{ .name = "pwrite", .ret_type = 1, .nargs = 4,
+	  .args = { { Int, 0 }, { BinString | IN, 1 }, { Int, 2 }, { Quad, 3 + QUAD_ALIGN } } },
+	{ .name = "writev", .ret_type = 1, .nargs = 3,
+	  .args = { { Int, 0 }, { Iovec | IN, 1 }, { Int, 2 } } },
 	{ .name = "ioctl", .ret_type = 1, .nargs = 3,
 	  .args = { { Int, 0 }, { Ioctl, 1 }, { Hex, 2 } } },
 	{ .name = "break", .ret_type = 1, .nargs = 1,
@@ -163,19 +174,23 @@
 	{ .name = "sigaction", .ret_type = 1, .nargs = 3,
 	  .args = { { Signal, 0 }, { Sigaction | IN, 1 }, { Sigaction | OUT, 2 } } },
 	{ .name = "accept", .ret_type = 1, .nargs = 3,
-	  .args = { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } },
+	  .args = { { Int, 0 }, { Sockaddr | OUT, 1 }, { Int32Ptr | OUT, 2 } } },
 	{ .name = "bind", .ret_type = 1, .nargs = 3,
 	  .args = { { Int, 0 }, { Sockaddr | IN, 1 }, { Int, 2 } } },
 	{ .name = "connect", .ret_type = 1, .nargs = 3,
 	  .args = { { Int, 0 }, { Sockaddr | IN, 1 }, { Int, 2 } } },
 	{ .name = "getpeername", .ret_type = 1, .nargs = 3,
-	  .args = { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } },
+	  .args = { { Int, 0 }, { Sockaddr | OUT, 1 }, { Int32Ptr | OUT, 2 } } },
 	{ .name = "getsockname", .ret_type = 1, .nargs = 3,
-	  .args = { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } },
+	  .args = { { Int, 0 }, { Sockaddr | OUT, 1 }, { Int32Ptr | OUT, 2 } } },
 	{ .name = "recvfrom", .ret_type = 1, .nargs = 6,
-	  .args = { { Int, 0 }, { BinString | OUT, 1 }, { Int, 2 }, { Hex, 3 }, { Sockaddr | OUT, 4 }, { Ptr | OUT, 5 } } },
+	  .args = { { Int, 0 }, { BinString | OUT, 1 }, { Int, 2 }, { Msgflags, 3 }, { Sockaddr | OUT, 4 }, { Int32Ptr | OUT, 5 } } },
 	{ .name = "sendto", .ret_type = 1, .nargs = 6,
-	  .args = { { Int, 0 }, { BinString | IN, 1 }, { Int, 2 }, { Hex, 3 }, { Sockaddr | IN, 4 }, { Ptr | IN, 5 } } },
+	  .args = { { Int, 0 }, { BinString | IN, 1 }, { Int, 2 }, { Msgflags, 3 }, { Sockaddr | IN, 4 }, { Int32Ptr | IN, 5 } } },
+	{ .name = "recvmsg", .ret_type = 1, .nargs = 3,
+	  .args = { { Int, 0 }, { Ptr, 1 }, { Msgflags, 2 } } },
+	{ .name = "sendmsg", .ret_type = 1, .nargs = 3,
+	  .args = { { Int, 0 }, { Ptr, 1 }, { Msgflags, 2 } } },
 	{ .name = "execve", .ret_type = 1, .nargs = 3,
 	  .args = { { Name | IN, 0 }, { StringArray | IN, 1 }, { StringArray | IN, 2 } } },
 	{ .name = "linux_execve", .ret_type = 1, .nargs = 3,
@@ -201,7 +216,7 @@
 	{ .name = "gettimeofday", .ret_type = 1, .nargs = 2,
 	  .args = { { Timeval | OUT, 0 }, { Ptr, 1 } } },
 	{ .name = "clock_gettime", .ret_type = 1, .nargs = 2,
-	  .args = { { Int, 0 }, { Timespec | OUT, 1 } } },
+	  .args = { { Clockid, 0 }, { Timespec | OUT, 1 } } },
 	{ .name = "getitimer", .ret_type = 1, .nargs = 2,
 	  .args = { { Int, 0 }, { Itimerval | OUT, 2 } } },
 	{ .name = "setitimer", .ret_type = 1, .nargs = 3,
@@ -237,27 +252,57 @@
 	{ .name = "futimes", .ret_type = 1, .nargs = 2,
 	  .args = { { Int, 0 }, { Timeval | IN, 1 } } },
 	{ .name = "chflags", .ret_type = 1, .nargs = 2,
-	  .args = { { Name | IN, 0 }, { Hex, 1 } } },
+	  .args = { { Name | IN, 0 }, { Chflags | IN, 1 } } },
 	{ .name = "lchflags", .ret_type = 1, .nargs = 2,
-	  .args = { { Name | IN, 0 }, { Hex, 1 } } },
+	  .args = { { Name | IN, 0 }, { Chflags | IN, 1 } } },
+	{ .name = "fchflags", .ret_type = 1, .nargs = 2,
+	  .args = { { Int | IN, 0 }, { Chflags | IN, 1 } } },
 	{ .name = "pathconf", .ret_type = 1, .nargs = 2,
 	  .args = { { Name | IN, 0 }, { Pathconf, 1 } } },
-	{ .name = "pipe", .ret_type = 1, .nargs = 1,
-	  .args = { { Ptr, 0 } } },
+	{ .name = "pipe", .ret_type = 1, .nargs = 0 },
 	{ .name = "truncate", .ret_type = 1, .nargs = 3,
-	  .args = { { Name | IN, 0 }, { Int | IN, 1 }, { Quad | IN, 2 } } },
+	  .args = { { Name | IN, 0 }, { Int | IN, 1 }, { Quad | IN, 2 + QUAD_ALIGN } } },
 	{ .name = "ftruncate", .ret_type = 1, .nargs = 3,
-	  .args = { { Int | IN, 0 }, { Int | IN, 1 }, { Quad | IN, 2 } } },
+	  .args = { { Int | IN, 0 }, { Int | IN, 1 }, { Quad | IN, 2 + QUAD_ALIGN } } },
 	{ .name = "kill", .ret_type = 1, .nargs = 2,
 	  .args = { { Int | IN, 0 }, { Signal | IN, 1 } } },
 	{ .name = "munmap", .ret_type = 1, .nargs = 2,
 	  .args = { { Ptr, 0 }, { Int, 1 } } },
 	{ .name = "read", .ret_type = 1, .nargs = 3,
 	  .args = { { Int, 0 }, { BinString | OUT, 1 }, { Int, 2 } } },
+	{ .name = "pread", .ret_type = 1, .nargs = 4,
+	  .args = { { Int, 0 }, { BinString | OUT, 1 }, { Int, 2 }, { Quad, 3 + QUAD_ALIGN } } },
+	{ .name = "readv", .ret_type = 1, .nargs = 3,
+	  .args = { { Int, 0 }, { Iovec | OUT, 1 }, { Int, 2 } } },
 	{ .name = "rename", .ret_type = 1, .nargs = 2,
 	  .args = { { Name , 0 } , { Name, 1 } } },
 	{ .name = "symlink", .ret_type = 1, .nargs = 2,
 	  .args = { { Name , 0 } , { Name, 1 } } },
+	{ .name = "_umtx_op", .ret_type = 1, .nargs = 5,
+	  .args = { { UmtxArg1, 0 }, { Umtxop, 1 } , { Int , 2 } , { UmtxArg4, 3 }, { UmtxArg5, 4 } } },
+	{ .name = "ptrace", .ret_type = 1, .nargs = 4,
+	  .args = { { Ptrace, 0 }, { Int, 1 } , { Ptr , 2 } , { Int, 3 } } },
+	{ .name = "wait4", .ret_type = 1, .nargs = 4,
+	  .args = { { Int, 0 }, { Waitstat | OUT, 1 } , { Waitopts , 2 } , { Rusage, 3 } } },
+	{ .name = "waitpid", .ret_type = 1, .nargs = 3,
+	  .args = { { Int, 0 }, { Waitstat | OUT, 1 } , { Waitopts , 2 } } },
+	{ .name = "sigreturn", .ret_type = 0, .nargs = 1,
+	  .args = { { Ptr, 0 } } },
+	{ .name = "madvise", .ret_type = 1, .nargs = 3,
+	  .args = { { Ptr, 0 }, { Int, 1 } , { Madvise , 2 } } },
+	{ .name = "thr_set_name", .ret_type = 1, .nargs = 2,
+	  .args = { { Int, 0 }, { Name, 1 } } },
+	{ .name = "sendfile", .ret_type = 1, .nargs = 7,
+	  .args = { { Int, 0 }, 
+	    { Int, 1 }, 
+	    { Quad, 2 + QUAD_ALIGN },
+	    { Int, 2 + QUAD_SLOTS + QUAD_ALIGN },
+	    { Sendfilehdtr, 3 + QUAD_SLOTS + QUAD_ALIGN }, 
+	    { Int32Ptr | OUT, 4 + QUAD_SLOTS + QUAD_ALIGN }, 
+	    { Sendfileflags, 5 + QUAD_SLOTS + QUAD_ALIGN } } },
+	{ .name = "utrace", .ret_type = 1, .nargs = 2,
+	  .args = { { Utrace | IN, 0 }, { Int, 1 } } },
+	    
 	{ .name = 0 },
 };
 
@@ -317,6 +362,7 @@
 };
 
 static struct xlat fcntlfl_arg[] = {
+	X(FREAD) X(FWRITE) /* these two can't be set but may be returned by GETFL */
 	X(O_APPEND) X(O_ASYNC) X(O_FSYNC) X(O_NONBLOCK) X(O_NOFOLLOW)
 	X(O_DIRECT) XEND
 };
@@ -367,6 +413,65 @@
 	XEND
 };
 
+static struct xlat umtxop_arg[] = {
+	X(UMTX_OP_LOCK) X(UMTX_OP_UNLOCK) X(UMTX_OP_WAIT) X(UMTX_OP_WAKE)
+	X(UMTX_OP_MUTEX_TRYLOCK) X(UMTX_OP_MUTEX_LOCK)
+	X(UMTX_OP_MUTEX_UNLOCK) X(UMTX_OP_SET_CEILING) X(UMTX_OP_CV_WAIT)
+	X(UMTX_OP_CV_SIGNAL) X(UMTX_OP_CV_BROADCAST) X(UMTX_OP_WAIT_UINT)
+	X(UMTX_OP_RW_RDLOCK) X(UMTX_OP_RW_WRLOCK) X(UMTX_OP_RW_UNLOCK)
+	XEND
+};
+
+static struct xlat msg_flags[] = {
+	X(MSG_OOB) X(MSG_PEEK) X(MSG_DONTROUTE) X(MSG_EOR) X(MSG_TRUNC)
+	X(MSG_CTRUNC) X(MSG_WAITALL) X(MSG_DONTWAIT) X(MSG_EOF)
+	X(MSG_NBIO) X(MSG_COMPAT) X(MSG_NOSIGNAL)
+	XEND
+};
+
+static struct xlat ptrace_arg[] = {
+	X(PT_TRACE_ME) X(PT_READ_I) X(PT_READ_D) X(PT_WRITE_I)
+	X(PT_WRITE_D) X(PT_CONTINUE) X(PT_KILL) X(PT_STEP)
+	X(PT_ATTACH) X(PT_DETACH) X(PT_IO) X(PT_LWPINFO) X(PT_GETNUMLWPS)
+	X(PT_GETLWPLIST) X(PT_CLEARSTEP) X(PT_SETSTEP) X(PT_SUSPEND)
+	X(PT_RESUME) X(PT_TO_SCE) X(PT_TO_SCX) X(PT_SYSCALL) X(PT_GETREGS)
+	X(PT_SETREGS) X(PT_GETFPREGS) X(PT_SETFPREGS) X(PT_GETDBREGS)
+	X(PT_SETDBREGS) X(PT_FIRSTMACH)
+	XEND
+};
+
+static struct xlat clockid_arg[] = {
+	X(CLOCK_REALTIME) X(CLOCK_VIRTUAL) X(CLOCK_PROF) X(CLOCK_MONOTONIC)
+	X(CLOCK_UPTIME) X(CLOCK_UPTIME_PRECISE) X(CLOCK_UPTIME_FAST)
+	X(CLOCK_REALTIME_PRECISE) X(CLOCK_REALTIME_FAST)
+	X(CLOCK_MONOTONIC_PRECISE) X(CLOCK_MONOTONIC_FAST) X(CLOCK_SECOND)
+	XEND
+};
+
+static struct xlat chflags_flags[] = {
+	X(UF_NODUMP) X(UF_IMMUTABLE) X(UF_APPEND) X(UF_OPAQUE)
+	X(UF_NOUNLINK) X(SF_ARCHIVED) X(SF_IMMUTABLE) X(SF_APPEND)
+	X(SF_NOUNLINK) X(SF_SNAPSHOT)
+	XEND
+};
+
+static struct xlat waitopts_flags[] = {
+	X(WCONTINUED) X(WNOHANG) X(WUNTRACED)
+	XEND
+};
+
+static struct xlat sendfile_flags[] = {
+	X(SF_NODISKIO)
+	XEND
+};
+
+static struct xlat madvise_arg[] = {
+	X(MADV_NORMAL) X(MADV_RANDOM) X(MADV_SEQUENTIAL) X(MADV_WILLNEED)
+	X(MADV_DONTNEED) X(MADV_FREE) X(MADV_NOSYNC) X(MADV_AUTOSYNC)
+	X(MADV_NOCORE) X(MADV_CORE) X(MADV_PROTECT)
+	XEND
+};
+
 #undef X
 #undef XEND
 
@@ -475,9 +580,54 @@
 	return (0);
 }
 
-#define MAXSIZE 4096
-#define BLOCKSIZE 1024
+
 /*
+ * get_binstring
+ *
+ * Read 'len' bytes starting at 'offset', convert them to a readable
+ * format truncating at 'maxlen' characters, and return the string as a
+ * malloc'ed pointer.  If flags is non-NULL and we had to truncate, set 
+ * BS_TRUNCATED.
+ */
+#define BS_TRUNCATED 0x01
+
+static char *
+get_binstring(int pid, void *offset, int len, int maxlen, int *flags) {
+  char *tmp;
+  char *tmp2, *tmp3;
+  int truncated = 0;
+
+  /* Don't print more than maxlen characters, to avoid wrapping a line.
+   * If we have to truncate, put '...' after the string.
+   */
+  if (len > maxlen) {
+    len = maxlen;
+    truncated = 1;
+  }
+  
+  tmp2 = malloc(maxlen+1);
+  if (len && tmp2 && get_struct(pid, offset, tmp2, len) != -1) {
+    tmp3 = malloc(len * 4 + 1);
+    while (len) {
+      if (strvisx(tmp3, tmp2, len, VIS_CSTYLE|VIS_TAB|VIS_NL) <= maxlen)
+        break;
+      len--;
+      truncated = 1;
+    }
+    asprintf(&tmp, "\"%s\"%s", tmp3, truncated ? "..." : "");
+    free(tmp3);
+  } else
+    asprintf(&tmp, "0x%p", offset);
+  if (tmp2)
+  	free(tmp2);
+  if (flags && truncated)
+  	*flags |= BS_TRUNCATED;
+  return tmp;
+}
+
+#define MAXSIZE 4096 
+#define BLOCKSIZE 1024 
+/*
  * get_string
  * Copy a string from the process.  Note that it is
  * expected to be a C string, but if max is set, it will
@@ -562,36 +712,14 @@
 		   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(pid, (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]);
-		}
+		tmp = get_binstring(pid, (void*)args[sc->offset], len, trussinfo->strsize, NULL);
 		break;
 	}
 	case StringArray: {
@@ -602,7 +730,8 @@
 
 		if (get_struct(pid, (void *)args[sc->offset], (void *)&strarray,
 			sizeof(strarray)) == -1) {
-			err(1, "get_struct %p", (void *)args[sc->offset]);
+			asprintf(&tmp, "0x%lx", args[sc->offset]);
+			break;
 		}
 		num = 0;
 		size = 0;
@@ -627,6 +756,93 @@
 		tmp2 += sprintf(tmp2, "]");
 		break;
 	}
+	case Iovec: {
+		/* 
+		 * XXX: An Iovec argument expects the /next/ syscall argument to
+		 * be the size of the array.  This matches the writev and readv
+		 * syscalls.
+		 */
+		struct sbuf *sb;
+		struct iovec *io;
+		int num = args[sc->offset+1];
+		int bytes = sizeof(struct iovec) * num;
+		sb = sbuf_new(NULL, NULL, 80, SBUF_AUTOEXTEND);
+		io = malloc(bytes);
+		
+		if (get_struct(pid, (void *)args[sc->offset], io, bytes) != -1) {
+		  int i;
+		  sbuf_putc(sb, '{');
+		  for (i = 0 ; i < num ; i++) {
+		    char *tmp2;
+		    int flags = 0;
+		    if (i) sbuf_cat(sb,", ");
+		    tmp2 = get_binstring(pid, io[i].iov_base, 
+		      io[i].iov_len, trussinfo->strsize, &flags);
+		if (tmp2) {
+		  /* If our string was truncated, print the iovec length
+		     after it */
+		  if (flags & BS_TRUNCATED)
+		    sbuf_printf(sb, "%s/%d", tmp2, io[i].iov_len);
+		  else
+		    sbuf_cat(sb, tmp2);
+		      free(tmp2);
+		    } else
+		      sbuf_printf(sb, "0x%p/%d", io[i].iov_base, io[i].iov_len);
+		  }
+		  sbuf_putc(sb, '}');
+		} else
+		  sbuf_printf(sb, "0x%lx", args[sc->offset]);
+		sbuf_finish(sb);
+		tmp = strdup(sbuf_data(sb));
+		sbuf_delete(sb);
+		free(io);
+		 
+		break;
+	}
+	case Sendfilehdtr: {
+		struct sf_hdtr sf;
+		struct sbuf *sb;
+		sb = sbuf_new(NULL, NULL, 80, SBUF_AUTOEXTEND);
+		if (sb && get_struct(pid, (void *)args[sc->offset], &sf, sizeof(sf)) != -1) {
+		  struct iovec *io;
+		  io = malloc(sizeof(struct iovec) * (sf.hdr_cnt + sf.trl_cnt + 1));
+		  sbuf_putc(sb, '{');
+		  if (io && get_struct(pid, (void *)sf.headers, 
+		      io, sizeof(struct iovec) * sf.hdr_cnt) != -1) {
+		    int i;
+		    sbuf_putc(sb, '{');
+		    for (i = 0 ; i < sf.hdr_cnt ; i++) {
+		      if (i) sbuf_cat(sb,", ");
+		      sbuf_printf(sb, "0x%p/%d", io[i].iov_base, io[i].iov_len);
+		    }
+		    sbuf_putc(sb, '}');
+		  } else
+		    sbuf_printf(sb, "0x%p", sf.headers);
+		  sbuf_printf(sb, ", %d,", sf.hdr_cnt);
+
+		  if (io && get_struct(pid, (void *)sf.trailers, 
+		      io, sizeof(struct iovec)*sf.trl_cnt) != -1) {
+		    int i;
+		    sbuf_putc(sb, '{');
+		    for (i = 0 ; i < sf.trl_cnt ; i++) {
+		      if (i) sbuf_cat(sb,", ");
+		      sbuf_printf(sb, "0x%p/%d", io[i].iov_base, io[i].iov_len);
+		    }
+		    sbuf_putc(sb, '}');
+		  } else
+		    sbuf_printf(sb, "0x%p", sf.headers);
+		  sbuf_printf(sb, ", %d", sf.trl_cnt);
+		  
+		  sbuf_putc(sb, '}');
+		  sbuf_finish(sb);
+		  tmp = strdup(sbuf_data(sb));
+		  sbuf_delete(sb);
+		  if (io) free(io);
+		} else
+		  asprintf(&tmp, "0x%lx", args[sc->offset]);
+		
+		break;
+	}
 #ifdef __LP64__
 	case Quad:
 		asprintf(&tmp, "0x%lx", args[sc->offset]);
@@ -642,17 +858,48 @@
 	case Ptr:
 		asprintf(&tmp, "0x%lx", args[sc->offset]);
 		break;
-	case Readlinkres: {
-		char *tmp2;
-		if (retval == -1) {
-			tmp = strdup("");
-			break;
+	case Int32Ptr: {
+		int arg;
+		if (get_struct(pid, (void *)args[sc->offset], &arg, sizeof(arg)) != -1) {
+			asprintf(&tmp, "{%d}", arg);
+		} else {
+			asprintf(&tmp, "0x%lx", args[sc->offset]);
 		}
-		tmp2 = get_string(pid, (void*)args[sc->offset], retval);
-		asprintf(&tmp, "\"%s\"", tmp2);
-		free(tmp2);
 		break;
 	}
+	case Waitstat: {
+		int arg;
+		if (get_struct(pid, (void *)args[sc->offset], &arg, sizeof(arg)) != -1) {
+			char *tmp1 = NULL;
+			if (WIFSTOPPED(arg)) {
+				tmp1 = strsig(WSTOPSIG(arg));
+				if (!tmp1) asprintf(&tmp1, "%d", WSTOPSIG(arg));
+				asprintf(&tmp, "{STOP %s}", tmp1);
+			} else if (WIFSIGNALED(arg)) {
+				tmp1 = strsig(WTERMSIG(arg));
+				if (!tmp1) asprintf(&tmp1, "%d", WTERMSIG(arg));
+				asprintf(&tmp, "{SIG %s%s}", tmp1,
+				    WCOREDUMP(arg)?" (core)":"");
+			} else if (WIFEXITED(arg)) {
+				asprintf(&tmp, "{EXIT %d}", WEXITSTATUS(arg));
+			} else {
+				asprintf(&tmp, "{%x}",arg);
+			}
+			if (tmp1) free(tmp1);
+		} else {
+		  asprintf(&tmp, "0x%lx", args[sc->offset]);
+		}
+		break;
+	}
+	case Waitopts:
+		tmp = strdup(xlookup_bits(waitopts_flags, args[sc->offset]));
+		break;
+	case Madvise:
+		tmp = strdup(xlookup(madvise_arg, args[sc->offset]));
+		break;
+	case Sendfileflags:
+		tmp = strdup(xlookup_bits(sendfile_flags, args[sc->offset]));
+		break;
 	case Ioctl: {
 		const char *temp = ioctlname(args[sc->offset]);
 		if (temp) {
@@ -674,10 +921,25 @@
 			asprintf(&tmp, "0x%lx", args[sc->offset]);
 		break;
 	}
+	case Umtxop:
+		tmp = strdup(xlookup(umtxop_arg, args[sc->offset]));
+		break;
+	case UmtxArg1:
+		asprintf(&tmp, "0x%lx", args[sc->offset]);
+		break;
+	case UmtxArg4:
+		asprintf(&tmp, "0x%lx", args[sc->offset]);
+		break;
+	case UmtxArg5:
+		asprintf(&tmp, "0x%lx", args[sc->offset]);
+		break;
+	case Clockid:
+		tmp = strdup(xlookup(clockid_arg, args[sc->offset]));
+		break;
 	case Timespec: {
 		struct timespec ts;
 		if (get_struct(pid, (void *)args[sc->offset], &ts, sizeof(ts)) != -1)
-			asprintf(&tmp, "{%ld.%09ld }", (long)ts.tv_sec, ts.tv_nsec);
+			asprintf(&tmp, "{%ld.%09ld}", (long)ts.tv_sec, ts.tv_nsec);
 		else
 			asprintf(&tmp, "0x%lx", args[sc->offset]);
 		break;
@@ -685,7 +947,7 @@
 	case Timeval: {
 		struct timeval tv;
 		if (get_struct(pid, (void *)args[sc->offset], &tv, sizeof(tv)) != -1)
-			asprintf(&tmp, "{%ld.%06ld }", (long)tv.tv_sec, tv.tv_usec);
+			asprintf(&tmp, "{%ld.%06ld}", (long)tv.tv_sec, tv.tv_usec);
 		else
 			asprintf(&tmp, "0x%lx", args[sc->offset]);
 		break;
@@ -693,7 +955,7 @@
 	case Timeval2: {
 		struct timeval tv[2];
 		if (get_struct(pid, (void *)args[sc->offset], &tv, sizeof(tv)) != -1)
-			asprintf(&tmp, "{%ld.%06ld, %ld.%06ld }",
+			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
@@ -703,7 +965,7 @@
 	case Itimerval: {
 		struct itimerval itv;
 		if (get_struct(pid, (void *)args[sc->offset], &itv, sizeof(itv)) != -1)
-			asprintf(&tmp, "{%ld.%06ld, %ld.%06ld }",
+			asprintf(&tmp, "{%ld.%06ld, %ld.%06ld}",
 			    (long)itv.it_interval.tv_sec,
 			    itv.it_interval.tv_usec,
 			    (long)itv.it_value.tv_sec,
@@ -723,14 +985,13 @@
 		int i, tmpsize, u, used;
 		const int per_fd = 100;
 
-		if ((pfd = malloc(bytes)) == NULL)
-			err(1, "Cannot malloc %d bytes for pollfd array", bytes);
-		if (get_struct(pid, (void *)args[sc->offset], pfd, bytes) != -1) {
+		pfd = malloc(bytes);
+		tmpsize = 1 + per_fd * numfds + 2;
+		tmp = malloc(tmpsize);
+		if (pfd && tmp && 
+		    get_struct(pid, (void *)args[sc->offset], pfd, bytes) != -1) {
 
 			used = 0;
-			tmpsize = 1 + per_fd * numfds + 2;
-			if ((tmp = malloc(tmpsize)) == NULL)
-				err(1, "Cannot alloc %d bytes for poll output", tmpsize);
 
 			tmp[used++] = '{';
 			for (i = 0; i < numfds; i++) {
@@ -746,9 +1007,10 @@
 			tmp[used++] = '}';
 			tmp[used++] = '\0';
 		} else {
+			if (tmp) free(tmp);
 			asprintf(&tmp, "0x%lx", args[sc->offset]);
 		}
-		free(pfd);
+		if (pfd) free(pfd);
 		break;
 	}
 	case Fd_set: {
@@ -762,13 +1024,12 @@
 		int i, tmpsize, u, used;
 		const int per_fd = 20;
 
-		if ((fds = malloc(bytes)) == NULL)
-			err(1, "Cannot malloc %d bytes for fd_set array", bytes);
-		if (get_struct(pid, (void *)args[sc->offset], fds, bytes) != -1) {
+		fds = malloc(bytes);
+		tmpsize = 1 + numfds * per_fd + 2;
+		tmp = malloc(tmpsize);
+		if (fds && tmp && 
+		    get_struct(pid, (void *)args[sc->offset], fds, bytes) != -1) {
 			used = 0;
-			tmpsize = 1 + numfds * per_fd + 2;
-			if ((tmp = malloc(tmpsize)) == NULL)
-				err(1, "Cannot alloc %d bytes for fd_set output", tmpsize);
 
 			tmp[used++] = '{';
 			for (i = 0; i < numfds; i++) {
@@ -783,9 +1044,10 @@
 			tmp[used++] = '}';
 			tmp[used++] = '\0';
 		} else {
+			if (tmp) free(tmp);
 			asprintf(&tmp, "0x%lx", args[sc->offset]);
 		}
-		free(fds);
+		if (fds) free(fds);
 		break;
 	}
 	case Signal: {
@@ -882,6 +1144,12 @@
 	case Pathconf:
 		tmp = strdup(xlookup(pathconf_arg, args[sc->offset]));
 		break;
+	case Msgflags:
+		tmp = strdup(xlookup_bits(msg_flags, args[sc->offset]));
+		break;
+	case Chflags:
+		tmp = strdup(xlookup_bits(chflags_flags, args[sc->offset]));
+		break;
 	case Sockaddr: {
 		struct sockaddr_storage ss;
 		char addr[64];
@@ -894,14 +1162,16 @@
 		int i;
 
 		if (args[sc->offset] == 0) {
-			asprintf(&tmp, "NULL");
+			tmp = strdup("0x0");
 			break;
 		}
 
 		/* yuck: get ss_len */
 		if (get_struct(pid, (void *)args[sc->offset], (void *)&ss,
-			sizeof(ss.ss_len) + sizeof(ss.ss_family)) == -1)
-			err(1, "get_struct %p", (void *)args[sc->offset]);
+		    sizeof(ss.ss_len) + sizeof(ss.ss_family)) == -1) {
+			asprintf(&tmp, "0x%lx", args[sc->offset]);
+			break;
+		}
 		/*
 		 * If ss_len is 0, then try to guess from the sockaddr type.
 		 * AF_UNIX may be initialized incorrectly, so always frob
@@ -922,7 +1192,8 @@
 		}
 		if (get_struct(pid, (void *)args[sc->offset], (void *)&ss, ss.ss_len)
 		    == -1) {
-			err(2, "get_struct %p", (void *)args[sc->offset]);
+			asprintf(&tmp, "0x%lx", args[sc->offset]);
+			break;
 		}
 
 		switch (ss.ss_family) {
@@ -996,22 +1267,23 @@
 		else if (sc->offset == 3 && retval != -1)
 			numevents = retval;
 
-		if (numevents >= 0)
+		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(pid, (void *)args[sc->offset], ke, bytes) != -1) {
+
+		tmpsize = 1 + per_ke * numevents + 2;
+		tmp = malloc(tmpsize);
+		ke = malloc(bytes+1);
+		
+		if (tmp && ke && numevents > 0 && 
+		    get_struct(pid, (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",
+				    "%s%d,%s,%s,%d,%p,%p",
 				    i > 0 ? " " : "",
-				    (void *)ke[i].ident,
+				    ke[i].ident,
 				    xlookup(kevent_filters, ke[i].filter),
 				    xlookup_bits(kevent_flags, ke[i].flags),
 				    ke[i].fflags,
@@ -1023,9 +1295,10 @@
 			tmp[used++] = '}';
 			tmp[used++] = '\0';
 		} else {
+			if (tmp) free(tmp);
 			asprintf(&tmp, "0x%lx", args[sc->offset]);
 		}
-		free(ke);
+		if (ke) free(ke);
 		break;
 	}
 	case Stat: {
@@ -1063,6 +1336,40 @@
 		}
 		break;
 	}
+	case Ptrace:
+		tmp = strdup(xlookup(ptrace_arg, args[sc->offset]));
+		break;
+	case Utrace: {
+		/* Ugly - guess based on various parameters what the buffer
+		   is  supposed to hold. */
+		int len;
+		char buf[80];
+		struct utrace_malloc {
+			void *p;
+			size_t s;
+			void *r;
+		} *ut;
+		
+		len = args[sc->offset + 1];
+		get_struct(pid,(void *)args[sc->offset],&buf, len<80?len:80);
+		
+		if (len == sizeof(struct utrace_malloc)) {
+			ut = (struct utrace_malloc *)&buf;
+			if (ut->p == NULL) {
+				if (ut->s == 0 && ut->r == NULL)
+					tmp=strdup("malloc_init()");
+				else
+					asprintf(&tmp,"%p = malloc(%zu)", ut->r, ut->s);
+			} else {
+				if (ut->s == 0)
+					asprintf(&tmp,"free(%p)", ut->p);
+				else
+					asprintf(&tmp,"%p = realloc(%p, %zu)", ut->r, ut->p, ut->s);
+			}
+		} else
+			tmp = get_binstring(pid, (void*)args[sc->offset], len, trussinfo->strsize, NULL);
+		break;	
+	}
 	default:
 		errx(1, "Invalid argument type %d\n", sc->type & ARG_MASK);
 	}
@@ -1083,27 +1390,42 @@
 	int len = 0;
 	struct timespec timediff;
 
-	if (trussinfo->flags & FOLLOWFORKS)
-		len += fprintf(trussinfo->outfile, "%5d: ", trussinfo->pid);
+	if ((trussinfo->curthread->flags & EXIT_ONLY) == 0) {
+		if (trussinfo->flags & FOLLOWFORKS)
+			len += fprintf(trussinfo->outfile, "%5d", trussinfo->pid);
 
-	if (name != NULL && (!strcmp(name, "execve") || !strcmp(name, "exit"))) {
-		clock_gettime(CLOCK_REALTIME, &trussinfo->after);
-	}
+		if (trussinfo->threadcount > 1)
+			len += fprintf(trussinfo->outfile, "/%d", trussinfo->curthread->tid/*threadnum*/);
 
-	if (trussinfo->flags & ABSOLUTETIMESTAMPS) {
-		timespecsubt(&trussinfo->after, &trussinfo->start_time, &timediff);
-		len += fprintf(trussinfo->outfile, "%ld.%09ld ",
-		    (long)timediff.tv_sec, timediff.tv_nsec);
+		if ((trussinfo->flags & FOLLOWFORKS) || (trussinfo->threadcount > 1))
+			len += fprintf(trussinfo->outfile, ": ");
+
+		if (name != NULL && (!strcmp(name, "execve") || !strcmp(name, "exit"))) {
+			clock_gettime(CLOCK_REALTIME, &trussinfo->after);
+		}
+
+		if (trussinfo->flags & ABSOLUTETIMESTAMPS) {
+			timespecsubt(&trussinfo->after, &trussinfo->start_time, &timediff);
+			len += fprintf(trussinfo->outfile, "%ld.%09ld ",
+			    (long)timediff.tv_sec, timediff.tv_nsec);
+		}
+
+		if (trussinfo->flags & RELATIVETIMESTAMPS) {
+			timespecsubt(&trussinfo->after, &trussinfo->before, &timediff);
+			len += fprintf(trussinfo->outfile, "%ld.%09ld ",
+			    (long)timediff.tv_sec, timediff.tv_nsec);
+		}
+
+		len += fprintf(trussinfo->outfile, "%s(", name);
 	}
 
-	if (trussinfo->flags & RELATIVETIMESTAMPS) {
-		timespecsubt(&trussinfo->after, &trussinfo->before, &timediff);
-		len += fprintf(trussinfo->outfile, "%ld.%09ld ",
-		    (long)timediff.tv_sec, timediff.tv_nsec);
+	if ((trussinfo->curthread->flags & ENTRY_ONLY)) {
+		fflush(trussinfo->outfile);
+		trussinfo->curthread->flags |= PRINTED_ENTRY;
+		trussinfo->curthread->flags &= ~ENTRY_ONLY;
+		return;
 	}
 
-	len += fprintf(trussinfo->outfile, "%s(", name);
-
 	for (i = 0; i < nargs; i++) {
 		if (s_args[i])
 			len += fprintf(trussinfo->outfile, "%s", s_args[i]);
Index: i386-fbsd.c
===================================================================
--- i386-fbsd.c	(revision 228100)
+++ i386-fbsd.c	(working copy)
@@ -49,6 +49,7 @@
 #include <machine/reg.h>
 #include <machine/psl.h>
 
+#include <err.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <signal.h>
@@ -77,29 +78,29 @@
  * 'struct syscall' describes the system call; it may be NULL, however,
  * if we don't know about this particular system call yet.
  */
-static struct freebsd_syscall {
+struct freebsd_syscall {
 	struct syscall *sc;
 	const char *name;
 	int number;
 	unsigned long *args;
 	int nargs;	/* number of arguments -- *not* number of words! */
 	char **s_args;	/* the printable arguments */
-} fsc;
+};
 
 /* Clear up and free parts of the fsc structure. */
 static __inline void
-clear_fsc(void) {
-  if (fsc.args) {
-    free(fsc.args);
+clear_fsc(struct freebsd_syscall *fsc) {
+  if (fsc->args) {
+    free(fsc->args);
   }
-  if (fsc.s_args) {
+  if (fsc->s_args) {
     int i;
-    for (i = 0; i < fsc.nargs; i++)
-      if (fsc.s_args[i])
-	free(fsc.s_args[i]);
-    free(fsc.s_args);
+    for (i = 0; i < fsc->nargs; i++)
+      if (fsc->s_args[i])
+	free(fsc->s_args[i]);
+    free(fsc->s_args);
   }
-  memset(&fsc, 0, sizeof(fsc));
+  memset(fsc, 0, sizeof(*fsc));
 }
 
 /*
@@ -117,9 +118,20 @@
   unsigned int parm_offset;
   struct syscall *sc = NULL;
   struct ptrace_io_desc iorequest;
+  struct freebsd_syscall *fsc;
+  
   cpid = trussinfo->curthread->tid;
 
-  clear_fsc();
+  fsc = trussinfo->curthread->fsc;
+  if (fsc == NULL)
+  {
+  	fsc = malloc(sizeof(*fsc));
+  	if (fsc == NULL)
+      errx(1, "cannot allocate syscall struct");
+    memset(fsc, 0, sizeof(*fsc));
+    trussinfo->curthread->fsc = fsc;
+  } else
+    clear_fsc(fsc);
   
   if (ptrace(PT_GETREGS, cpid, (caddr_t)&regs, 0) < 0)
   {
@@ -131,7 +143,7 @@
   /*
    * FreeBSD has two special kinds of system call redirctions --
    * SYS_syscall, and SYS___syscall.  The former is the old syscall()
-   * routine, basicly; the latter is for quad-aligned arguments.
+   * routine, basically; the latter is for quad-aligned arguments.
    */
   syscall_num = regs.r_eax;
   switch (syscall_num) {
@@ -143,19 +155,23 @@
     syscall_num = ptrace(PT_READ_D, cpid, (caddr_t)parm_offset, 0);
     parm_offset += sizeof(quad_t);
     break;
+  default:
+/*    fprintf(trussinfo->outfile, "Trapped syscall entry, but unknown syscall method %d\n", syscall_num); */
+    break;
   }
 
-  fsc.number = syscall_num;
-  fsc.name =
-    (syscall_num < 0 || syscall_num >= nsyscalls) ? NULL : syscallnames[syscall_num];
-  if (!fsc.name) {
-    fprintf(trussinfo->outfile, "-- UNKNOWN SYSCALL %d --\n", syscall_num);
+  fsc->number = syscall_num;
+  fsc->name =
+    (syscall_num < 0 || syscall_num > nsyscalls) ? NULL : syscallnames[syscall_num];
+  if (!fsc->name) {
+    fprintf(trussinfo->outfile, "-- UNKNOWN SYSCALL %u (0x%08x) --\n", syscall_num, syscall_num);
   }
+  trussinfo->curthread->syscallname = fsc->name;
 
-  if (fsc.name && (trussinfo->flags & FOLLOWFORKS)
-   && ((!strcmp(fsc.name, "fork")
-    || !strcmp(fsc.name, "rfork")
-    || !strcmp(fsc.name, "vfork"))))
+  if (fsc->name && (trussinfo->flags & FOLLOWFORKS)
+   && ((!strcmp(fsc->name, "fork")
+    || !strcmp(fsc->name, "rfork")
+    || !strcmp(fsc->name, "vfork"))))
   {
     trussinfo->curthread->in_fork = 1;
   }
@@ -163,29 +179,29 @@
   if (nargs == 0)
     return;
 
-  fsc.args = malloc((1+nargs) * sizeof(unsigned long));
+  fsc->args = malloc((1+nargs) * sizeof(unsigned long));
   iorequest.piod_op = PIOD_READ_D;
   iorequest.piod_offs = (void *)parm_offset;
-  iorequest.piod_addr = fsc.args;
+  iorequest.piod_addr = fsc->args;
   iorequest.piod_len = (1+nargs) * sizeof(unsigned long);
   ptrace(PT_IO, cpid, (caddr_t)&iorequest, 0);
   if (iorequest.piod_len == 0)
     return;
 
-  if (fsc.name)
-  	sc = get_syscall(fsc.name);
+  if (fsc->name)
+  	sc = get_syscall(fsc->name);
   if (sc) {
-    fsc.nargs = sc->nargs;
+    fsc->nargs = sc->nargs;
   } else {
 #if DEBUG
     fprintf(trussinfo->outfile, "unknown syscall %s -- setting args to %d\n",
-	   fsc.name, nargs);
+	   fsc->name, nargs);
 #endif
-    fsc.nargs = nargs;
+    fsc->nargs = nargs;
   }
 
-  fsc.s_args = calloc(1, (1+fsc.nargs) * sizeof(char*));
-  fsc.sc = sc;
+  fsc->s_args = calloc(1, (1+fsc->nargs) * sizeof(char*));
+  fsc->sc = sc;
 
   /*
    * At this point, we set up the system call arguments.
@@ -195,21 +211,21 @@
    * passed in *and* out, however.
    */
 
-  if (fsc.name) {
+  if (fsc->name) {
 
 #if DEBUG
-    fprintf(stderr, "syscall %s(", fsc.name);
+    fprintf(stderr, "syscall %s(", fsc->name);
 #endif
-    for (i = 0; i < fsc.nargs; i++) {
+    for (i = 0; i < fsc->nargs; i++) {
 #if DEBUG
       fprintf(stderr, "0x%x%s",
 	      sc
-	      ? fsc.args[sc->args[i].offset]
-	      : fsc.args[i],
-	      i < (fsc.nargs - 1) ? "," : "");
+	      ? fsc->args[sc->args[i].offset]
+	      : fsc->args[i],
+	      i < (fsc->nargs - 1) ? "," : "");
 #endif
       if (sc && !(sc->args[i].type & OUT)) {
-	fsc.s_args[i] = print_arg(&sc->args[i], fsc.args, 0, trussinfo);
+	fsc->s_args[i] = print_arg(&sc->args[i], fsc->args, 0, trussinfo);
       }
     }
 #if DEBUG
@@ -221,28 +237,34 @@
   fprintf(trussinfo->outfile, "\n");
 #endif
 
-  if (fsc.name != NULL &&
-      (!strcmp(fsc.name, "execve") || !strcmp(fsc.name, "exit"))) {
+  if (fsc->name != NULL &&
+      (!strcmp(fsc->name, "execve") || !strcmp(fsc->name, "exit"))) {
 
     /* XXX
      * This could be done in a more general
      * manner but it still wouldn't be very pretty.
      */
-    if (!strcmp(fsc.name, "execve")) {
+    if (!strcmp(fsc->name, "execve")) {
         if ((trussinfo->flags & EXECVEARGS) == 0)
-          if (fsc.s_args[1]) {
-            free(fsc.s_args[1]);
-            fsc.s_args[1] = NULL;
+          if (fsc->s_args[1]) {
+            free(fsc->s_args[1]);
+            fsc->s_args[1] = NULL;
           }
         if ((trussinfo->flags & EXECVEENVS) == 0)
-          if (fsc.s_args[2]) {
-            free(fsc.s_args[2]);
-            fsc.s_args[2] = NULL;
+          if (fsc->s_args[2]) {
+            free(fsc->s_args[2]);
+            fsc->s_args[2] = NULL;
           }
     }
 
   }
 
+/*
+  trussinfo->curthread->flags |= ENTRY_ONLY;
+  print_syscall(trussinfo, fsc->name, fsc->nargs, fsc->s_args);
+  fprintf(trussinfo->outfile, "\n"); fflush(trussinfo->outfile);
+*/
+
   return;
 }
 
@@ -261,9 +283,13 @@
   int i;
   int errorp;
   struct syscall *sc;
+  struct freebsd_syscall *fsc;
 
-  if (fsc.name == NULL)
+  fsc = trussinfo->curthread->fsc;
+  if (fsc == NULL || fsc->name == NULL) {
+    fprintf(trussinfo->outfile, "No syscall?\n"); fflush(trussinfo->outfile);
     return (-1);
+  }
   cpid = trussinfo->curthread->tid;
 
   if (ptrace(PT_GETREGS, cpid, (caddr_t)&regs, 0) < 0)
@@ -280,10 +306,10 @@
    * stand some significant cleaning.
    */
 
-  sc = fsc.sc;
+  sc = fsc->sc;
   if (!sc) {
-    for (i = 0; i < fsc.nargs; i++)
-      asprintf(&fsc.s_args[i], "0x%lx", fsc.args[i]);
+    for (i = 0; i < fsc->nargs; i++)
+      asprintf(&fsc->s_args[i], "0x%lx", fsc->args[i]);
   } else {
     /*
      * Here, we only look for arguments that have OUT masked in --
@@ -297,16 +323,29 @@
 	 * it may not be valid.
 	 */
 	if (errorp)
-	  asprintf(&temp, "0x%lx", fsc.args[sc->args[i].offset]);
+	  asprintf(&temp, "0x%lx", fsc->args[sc->args[i].offset]);
 	else
-	  temp = print_arg(&sc->args[i], fsc.args, retval, trussinfo);
-	fsc.s_args[i] = temp;
+	  temp = print_arg(&sc->args[i], fsc->args, retval, trussinfo);
+	fsc->s_args[i] = temp;
       }
     }
   }
 
-  if (fsc.name != NULL &&
-      (!strcmp(fsc.name, "execve") || !strcmp(fsc.name, "exit"))) {
+  /*
+   * 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;
+  }
+
+  if (fsc->name != NULL &&
+      (!strcmp(fsc->name, "execve") || !strcmp(fsc->name, "exit"))) {
 	trussinfo->curthread->in_syscall = 1;
   }
 
@@ -315,9 +354,9 @@
    * but that complicates things considerably.
    */
 
-  print_syscall_ret(trussinfo, fsc.name, fsc.nargs, fsc.s_args, errorp,
-		    retval, fsc.sc);
-  clear_fsc();
+  print_syscall_ret(trussinfo, fsc->name, fsc->nargs, fsc->s_args, errorp,
+		    retval, fsc->sc);
+  clear_fsc(fsc);
 
   return (retval);
 }
Index: main.c
===================================================================
--- main.c	(revision 228100)
+++ main.c	(working copy)
@@ -60,7 +60,7 @@
 #include "extern.h"
 #include "syscall.h"
 
-#define MAXARGS 6
+#define MAXARGS 7	/* sendfile is 7 args */
 
 static void
 usage(void)
@@ -109,6 +109,12 @@
 	{ 0, 0, 0 },
 };
 
+/* dummy signal handler so we can break out of wait */
+static void trapalrm(int signo __unused)
+{
+	return;
+}
+
 /*
  * Set the execution type.  This is called after every exec, and when
  * a process is first monitored. 
@@ -184,9 +190,11 @@
 		errx(1, "calloc() failed");
 
 	trussinfo->outfile = stderr;
+	setlinebuf(trussinfo->outfile);
 	trussinfo->strsize = 32;
 	trussinfo->pr_why = S_NONE;
 	trussinfo->curthread = NULL;
+	trussinfo->threadcount = 0;
 	SLIST_INIT(&trussinfo->threadlist);
 	while ((c = getopt(ac, av, "p:o:facedDs:S")) != -1) {
 		switch (c) {
@@ -266,6 +274,8 @@
 		signal(SIGTERM, restore_proc);
 		signal(SIGQUIT, restore_proc);
 	}
+	signal(SIGALRM, trapalrm);
+	siginterrupt(SIGALRM, 1);
 
 
 	/*
Index: truss.h
===================================================================
--- truss.h	(revision 228100)
+++ truss.h	(working copy)
@@ -34,13 +34,20 @@
 #define EXECVEARGS         0x00000010
 #define EXECVEENVS         0x00000020
 #define COUNTONLY          0x00000040
+#define ENTRY_ONLY         0x00000080 /* Print syscall name only */
+#define PRINTED_ENTRY      0x00000100 /* Printed syscall name already */
+#define EXIT_ONLY          0x00000200 /* Print syscall args & retval only */
 
 struct threadinfo
 {
 	SLIST_ENTRY(threadinfo) entries;
 	lwpid_t tid;
+	int threadnum;	/* counts from 1 (i.e. this is not threadid) */
 	int in_syscall;
+	const char *syscallname;
+	void *fsc;
 	int in_fork;
+	int flags;
 };
 
 struct trussinfo
@@ -50,6 +57,7 @@
 	int pr_why;
 	int pr_data;
 	int strsize;
+	int threadcount;	/* Number of threads seen in this process */
 	FILE *outfile;
 
 	struct timespec start_time;
