[BACK]Return to kdump.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / kdump

File: [local] / src / usr.bin / kdump / kdump.c (download)

Revision 1.162, Fri Mar 29 07:53:32 2024 UTC (7 weeks, 2 days ago) by deraadt
Branch: MAIN
Changes since 1.161: +1 -2 lines

SYS_msyscall will go away soon.  kdump does not need a special handler
for it.
ok tb

/*	$OpenBSD: kdump.c,v 1.162 2024/03/29 07:53:32 deraadt Exp $	*/

/*-
 * Copyright (c) 1988, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <sys/time.h>
#include <sys/signal.h>
#include <sys/uio.h>
#include <sys/ktrace.h>
#include <sys/ioctl.h>
#include <sys/malloc.h>
#include <sys/namei.h>
#include <sys/ptrace.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <sys/siginfo.h>
#include <sys/vmmeter.h>
#include <sys/tty.h>
#include <sys/wait.h>
#define PLEDGENAMES
#include <sys/pledge.h>
#undef PLEDGENAMES
#define _KERNEL
#include <errno.h>
#undef _KERNEL
#include <ddb/db_var.h>
#include <machine/cpu.h>

#include <ctype.h>
#include <err.h>
#include <fcntl.h>
#include <limits.h>
#include <netdb.h>
#include <poll.h>
#include <signal.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <vis.h>

#include "ktrace.h"
#include "kdump.h"
#include "kdump_subr.h"
#include "extern.h"

#define nitems(_a)	(sizeof((_a)) / sizeof((_a)[0]))

enum {
	TIMESTAMP_NONE,
	TIMESTAMP_ABSOLUTE,
	TIMESTAMP_RELATIVE,
	TIMESTAMP_ELAPSED
} timestamp = TIMESTAMP_NONE;

int decimal, iohex, fancy = 1, maxdata = INT_MAX;
int needtid, tail, basecol;
char *tracefile = DEF_TRACEFILE;
struct ktr_header ktr_header;
pid_t pid_opt = -1;
const char *program;
char* utracefilter;

#define eqs(s1, s2)	(strcmp((s1), (s2)) == 0)

#include <sys/syscall.h>

#define KTRACE
#define PTRACE
#define NFSCLIENT
#define NFSSERVER
#define SYSVSEM
#define SYSVMSG
#define SYSVSHM
#define ACCOUNTING
#include <kern/syscalls.c>
#undef KTRACE
#undef PTRACE
#undef NFSCLIENT
#undef NFSSERVER
#undef SYSVSEM
#undef SYSVMSG
#undef SYSVSHM
#undef ACCOUNTING


static char *ptrace_ops[] = {
	"PT_TRACE_ME",	"PT_READ_I",	"PT_READ_D",	"PT_READ_U",
	"PT_WRITE_I",	"PT_WRITE_D",	"PT_WRITE_U",	"PT_CONTINUE",
	"PT_KILL",	"PT_ATTACH",	"PT_DETACH",	"PT_IO",
	"PT_SET_EVENT_MASK", "PT_GET_EVENT_MASK", "PT_GET_PROCESS_STATE",
	"PT_GET_THREAD_FIRST", "PT_GET_THREAD_NEXT",
};

static int fread_tail(void *, size_t, size_t);
static void dumpheader(struct ktr_header *);
static void ktrgenio(struct ktr_genio *, size_t);
static void ktrnamei(const char *, size_t);
static void ktrpsig(struct ktr_psig *);
static void ktrsyscall(struct ktr_syscall *, size_t);
static const char *kresolvsysctl(int, const int *);
static void ktrsysret(struct ktr_sysret *, size_t);
static void ktruser(struct ktr_user *, size_t);
static void ktrexec(const char*, size_t);
static void ktrpledge(struct ktr_pledge *, size_t);
static void ktrpinsyscall(struct ktr_pinsyscall *, size_t);
static void usage(void);
static void ioctldecode(int);
static void ptracedecode(int);
static void atfd(int);
static void polltimeout(int);
static void wait4pid(int);
static void signame(int);
static void semctlname(int);
static void shmctlname(int);
static void semgetname(int);
static void flagsandmodename(int);
static void clockname(int);
static void sockoptlevelname(int);
static void ktraceopname(int);
static void idtypeandid(int);

static int screenwidth;

int
main(int argc, char *argv[])
{
	int ch, silent;
	size_t ktrlen, size;
	int trpoints = ALL_POINTS;
	const char *errstr;
	void *m;

	if (screenwidth == 0) {
		struct winsize ws;

		if (fancy && ioctl(fileno(stderr), TIOCGWINSZ, &ws) != -1 &&
		    ws.ws_col > 8)
			screenwidth = ws.ws_col;
		else
			screenwidth = 80;
	}

	while ((ch = getopt(argc, argv, "f:dHlm:nP:p:RTt:u:xX")) != -1)
		switch (ch) {
		case 'f':
			tracefile = optarg;
			break;
		case 'd':
			decimal = 1;
			break;
		case 'H':
			needtid = 1;
			break;
		case 'l':
			tail = 1;
			break;
		case 'm':
			maxdata = strtonum(optarg, 0, INT_MAX, &errstr);
			if (errstr)
				errx(1, "-m %s: %s", optarg, errstr);
			break;
		case 'n':
			fancy = 0;
			break;
		case 'P':
			program = optarg;
			break;
		case 'p':
			pid_opt = strtonum(optarg, 1, INT_MAX, &errstr);
			if (errstr)
				errx(1, "-p %s: %s", optarg, errstr);
			break;
		case 'R':	/* relative timestamp */
			if (timestamp == TIMESTAMP_ABSOLUTE)
				timestamp = TIMESTAMP_ELAPSED;
			else
				timestamp = TIMESTAMP_RELATIVE;
			break;
		case 'T':
			if (timestamp == TIMESTAMP_RELATIVE)
				timestamp = TIMESTAMP_ELAPSED;
			else
				timestamp = TIMESTAMP_ABSOLUTE;
			break;
		case 't':
			trpoints = getpoints(optarg, DEF_POINTS);
			if (trpoints < 0)
				errx(1, "unknown trace point in %s", optarg);
			utracefilter = NULL;
			break;
		case 'u':
			utracefilter = optarg;
			trpoints = KTRFAC_USER;
			break;
		case 'x':
			iohex = 1;
			break;
		case 'X':
			iohex = 2;
			break;
		default:
			usage();
		}
	if (argc > optind)
		usage();

	if (strcmp(tracefile, "-") != 0)
		if (unveil(tracefile, "r") == -1)
			err(1, "unveil %s", tracefile);
	if (unveil(_PATH_PROTOCOLS, "r") == -1)
		err(1, "unveil %s", _PATH_PROTOCOLS);
	if (pledge("stdio rpath getpw", NULL) == -1)
		err(1, "pledge");

	m = malloc(size = 1025);
	if (m == NULL)
		err(1, NULL);
	if (strcmp(tracefile, "-") != 0)
		if (!freopen(tracefile, "r", stdin))
			err(1, "%s", tracefile);

	if (fread_tail(&ktr_header, sizeof(struct ktr_header), 1) == 0 ||
	    ktr_header.ktr_type != htobe32(KTR_START))
		errx(1, "%s: not a dump", tracefile);
	while (fread_tail(&ktr_header, sizeof(struct ktr_header), 1)) {
		silent = 0;
		if (pid_opt != -1 && pid_opt != ktr_header.ktr_pid)
			silent = 1;
		if (program != NULL &&
		    strcmp(ktr_header.ktr_comm, program) != 0)
			silent = 1;
		if (utracefilter == NULL && silent == 0 &&
		    trpoints & (1<<ktr_header.ktr_type))
			dumpheader(&ktr_header);
		ktrlen = ktr_header.ktr_len;
		if (ktrlen > size) {
			void *newm;

			if (ktrlen == SIZE_MAX)
				errx(1, "data too long");
			newm = realloc(m, ktrlen+1);
			if (newm == NULL)
				err(1, "realloc");
			m = newm;
			size = ktrlen;
		}
		if (ktrlen && fread_tail(m, ktrlen, 1) == 0)
			errx(1, "data too short");
		if (silent)
			continue;
		if ((trpoints & (1<<ktr_header.ktr_type)) == 0)
			continue;
		switch (ktr_header.ktr_type) {
		case KTR_SYSCALL:
			ktrsyscall(m, ktrlen);
			break;
		case KTR_SYSRET:
			ktrsysret(m, ktrlen);
			break;
		case KTR_NAMEI:
			ktrnamei(m, ktrlen);
			break;
		case KTR_GENIO:
			ktrgenio(m, ktrlen);
			break;
		case KTR_PSIG:
			ktrpsig(m);
			break;
		case KTR_STRUCT:
			ktrstruct(m, ktrlen);
			break;
		case KTR_USER:
			ktruser(m, ktrlen);
			break;
		case KTR_EXECARGS:
		case KTR_EXECENV:
			ktrexec(m, ktrlen);
			break;
		case KTR_PLEDGE:
			ktrpledge(m, ktrlen);
			break;
		case KTR_PINSYSCALL:
			ktrpinsyscall(m, ktrlen);
			break;
		default:
			printf("\n");
			break;
		}
		if (tail)
			(void)fflush(stdout);
	}
	exit(0);
}

static int
fread_tail(void *buf, size_t size, size_t num)
{
	int i;

	while ((i = fread(buf, size, num, stdin)) == 0 && tail) {
		(void)sleep(1);
		clearerr(stdin);
	}
	return (i);
}

static void
dumpheader(struct ktr_header *kth)
{
	static struct timespec prevtime;
	char unknown[64], *type;
	struct timespec temp;

	switch (kth->ktr_type) {
	case KTR_SYSCALL:
		type = "CALL";
		break;
	case KTR_SYSRET:
		type = "RET ";
		break;
	case KTR_NAMEI:
		type = "NAMI";
		break;
	case KTR_GENIO:
		type = "GIO ";
		break;
	case KTR_PSIG:
		type = "PSIG";
		break;
	case KTR_STRUCT:
		type = "STRU";
		break;
	case KTR_USER:
		type = "USER";
		break;
	case KTR_EXECARGS:
		type = "ARGS";
		break;
	case KTR_EXECENV:
		type = "ENV ";
		break;
	case KTR_PLEDGE:
		type = "PLDG";
		break;
	case KTR_PINSYSCALL:
		type = "PINS";
		break;
	default:
		/* htobe32() not guaranteed to work as case label */
		if (kth->ktr_type == htobe32(KTR_START)) {
			type = "STRT";
			break;
		}
		(void)snprintf(unknown, sizeof unknown, "UNKNOWN(%u)",
		    kth->ktr_type);
		type = unknown;
	}

	basecol = printf("%6ld", (long)kth->ktr_pid);
	if (needtid)
		basecol += printf("/%-7ld", (long)kth->ktr_tid);
	basecol += printf(" %-8s ", kth->ktr_comm);
	if (timestamp != TIMESTAMP_NONE) {
		if (timestamp == TIMESTAMP_ELAPSED) {
			if (prevtime.tv_sec == 0)
				prevtime = kth->ktr_time;
			timespecsub(&kth->ktr_time, &prevtime, &temp);
		} else if (timestamp == TIMESTAMP_RELATIVE) {
			timespecsub(&kth->ktr_time, &prevtime, &temp);
			prevtime = kth->ktr_time;
		} else
			temp = kth->ktr_time;
		basecol += printf("%lld.%06ld ", (long long)temp.tv_sec,
		    temp.tv_nsec / 1000);
	}
	basecol += printf("%s  ", type);
}

/*
 * Base Formatters
 */

/* some syscalls have padding that shouldn't be shown */
static int
pad(long arg)
{
	/* nothing printed */
	return (1);
}

/* a formatter that just saves the argument for the next formatter */
int arg1;
static int
pass_two(long arg)
{
	arg1 = (int)arg;

	/* nothing printed */
	return (1);
}

static int
pdeclong(long arg)
{
	(void)printf("%ld", arg);
	return (0);
}

static int
pdeculong(long arg)
{
	(void)printf("%lu", arg);
	return (0);
}

static int
phexlong(long arg)
{
	(void)printf("%#lx", arg);
	return (0);
}

static int
pnonfancy(long arg)
{
	if (decimal)
		(void)printf("%ld", arg);
	else
		(void)printf("%#lx", arg);
	return (0);
}

static void
pdecint(int arg)
{
	(void)printf("%d", arg);
}

static void
pdecuint(int arg)
{
	(void)printf("%u", arg);
}

static void
phexint(int arg)
{
	(void)printf("%#x", arg);
}

static void
poctint(int arg)
{
	(void)printf("%#o", arg);
}


#ifdef __LP64__

/* on LP64, long long arguments are the same as long arguments */
#define Phexlonglong	Phexlong
#define phexll		NULL		/* not actually used on LP64 */

/* no padding before long long arguments, nor at end */
#define PAD64		0
#define END64		end_of_args

#else /* __LP64__ */

/* on ILP32, long long arguments are passed as two 32bit args */
#define Phexlonglong	PASS_LONGLONG, Phexll

static int
phexll(long arg2)
{
	long long val;

#if _BYTE_ORDER == _LITTLE_ENDIAN
	val = ((long long)arg2 << 32) | ((long long)arg1 & 0xffffffff);
#else
	val = ((long long)arg1 << 32) | ((long long)arg2 & 0xffffffff);
#endif

	if (fancy || !decimal)
		(void)printf("%#llx", val);
	else
		(void)printf("%lld", val);
	return (0);
}

/*
 * Some ILP32 archs naturally align off_t arguments to 8byte boundaries
 * Get the compiler to tell if this arch is one of them.
 */
struct padding_test {
	int padtest_one;
	off_t padtest_two;
};
#define PAD64	(offsetof(struct padding_test,padtest_two) == 8)
#define END64	(PAD64 ? PASS_LONGLONG : end_of_args)

#endif /* __LP64__ */

static int (*long_formatters[])(long) = {
	NULL,
	pdeclong,
	pdeculong,
	phexlong,
	pass_two,
	pass_two,
	phexll,
	pad,
	pnonfancy,
};

static void (*formatters[])(int) = {
	NULL,
	pdecint,
	phexint,
	poctint,
	pdecuint,
	ioctldecode,
	ptracedecode,
	atfd,
	polltimeout,
	wait4pid,
	signame,
	semctlname,
	shmctlname,
	semgetname,
	flagsandmodename,
	clockname,
	sockoptlevelname,
	ktraceopname,
	fcntlcmdname,
	modename,
	flagsname,
	openflagsname,
	atflagsname,
	accessmodename,
	mmapprotname,
	mmapflagsname,
	wait4optname,
	sendrecvflagsname,
	mountflagsname,
	rebootoptname,
	flockname,
	sockoptname,
	sockipprotoname,
	socktypename,
	sockflagsname,
	sockfamilyname,
	mlockallname,
	shmatname,
	whencename,
	pathconfname,
	rlimitname,
	shutdownhowname,
	prioname,
	madvisebehavname,
	msyncflagsname,
	clocktypename,
	rusagewho,
	sigactionflagname,
	sigprocmaskhowname,
	minheritname,
	quotactlname,
	sigill_name,
	sigtrap_name,
	sigemt_name,
	sigfpe_name,
	sigbus_name,
	sigsegv_name,
	sigchld_name,
	ktracefacname,
	itimername,
	sigset,
	uidname,
	gidname,
	syslogflagname,
	futexflagname,
	waitidoptname,
	idtypeandid,
};

enum {
	/* the end of the (known) arguments is recognized by the zero fill */
	end_of_args	=  0,

	/* negative are the negative of the index into long_formatters[] */
	Pdeclong	= -1,
	Pdeculong	= -2,
	Phexlong	= -3,
	PASS_TWO	= -4,

/* the remaining long formatters still get called when non-fancy (-n option) */
#define FMT_IS_NONFANCY(x)	((x) <= PASS_LONGLONG)
	PASS_LONGLONG	= -5,
	Phexll		= -6,
	PAD		= -7,
	Pnonfancy	= -8,

	/* positive values are the index into formatters[] */
	Pdecint		= 1,
	Phexint,
	Poctint,
	Pdecuint,
	Ioctldecode,
	Ptracedecode,
	Atfd,
	Polltimeout,
	Wait4pid,
	Signame,
	Semctlname,
	Shmctlname,
	Semgetname,
	Flagsandmodename,
	Clockname,
	Sockoptlevelname,
	Ktraceopname,
	Fcntlcmdname,
	Modename,
	Flagsname,
	Openflagsname,
	Atflagsname,
	Accessmodename,
	Mmapprotname,
	Mmapflagsname,
	Wait4optname,
	Sendrecvflagsname,
	Mountflagsname,
	Rebootoptname,
	Flockname,
	Sockoptname,
	Sockipprotoname,
	Socktypename,
	Sockflagsname,
	Sockfamilyname,
	Mlockallname,
	Shmatname,
	Whencename,
	Pathconfname,
	Rlimitname,
	Shutdownhowname,
	Prioname,
	Madvisebehavname,
	Msyncflagsname,
	Clocktypename,
	Rusagewho,
	Sigactionflagname,
	Sigprocmaskhowname,
	Minheritname,
	Quotactlname,
	Sigill_name,
	Sigtrap_name,
	Sigemt_name,
	Sigfpe_name,
	Sigbus_name,
	Sigsegv_name,
	Sigchld_name,
	Ktracefacname,
	Itimername,
	Sigset,
	Uidname,
	Gidname,
	Syslogflagname,
	Futexflagname,
	Waitidoptname,
	Idtypeandid,
};

#define Pptr		Phexlong
#define	Psize		Pdeculong	/* size_t for small buffers */
#define	Pbigsize	Phexlong	/* size_t for I/O buffers */
#define Pcount		Pdecint		/* int for a count of something */
#define Pfd		Pdecint
#define Ppath		Phexlong
#define Pdev_t		Pdecint
#define Ppid_t		Pdecint
#define Ppgid		Pdecint		/* pid or negative pgid */
#define Poff_t		Phexlonglong
#define Pmsqid		Pdecint
#define Pshmid		Pdecint
#define Psemid		Pdecint
#define Pkey_t		Pdecint
#define Pucount		Pdecuint
#define Chflagsname	Phexlong	/* to be added */
#define Sockprotoname	Phexlong	/* to be added */
#define Swapctlname	Phexlong	/* to be added */
#define Msgflgname	Phexlong	/* to be added */


/* includes relevant entries as of syscalls.master rev 1.238 */
typedef signed char formatter;
static const formatter scargs[][8] = {
    [SYS_exit]		= { Pdecint },
    [SYS_read]		= { Pfd, Pptr, Pbigsize },
    [SYS_write]		= { Pfd, Pptr, Pbigsize },
    [SYS_open]		= { Ppath, PASS_TWO, Flagsandmodename },
    [SYS_close]		= { Pfd },
    [SYS_getentropy]	= { Pptr, Psize },
    [SYS___tfork]	= { Pptr, Psize },
    [SYS_link]		= { Ppath, Ppath },
    [SYS_unlink]	= { Ppath },
    [SYS_wait4]		= { Wait4pid, Pptr, Wait4optname },
    [SYS_chdir]		= { Ppath },
    [SYS_fchdir]	= { Pfd },
    [SYS_mknod]		= { Ppath, Modename, Pdev_t },
    [SYS_chmod]		= { Ppath, Modename },
    [SYS_chown]		= { Ppath, Uidname, Gidname },
    [SYS_break]		= { Pptr },
    [SYS_getrusage]	= { Rusagewho, Pptr },
    [SYS_mount]		= { Pptr, Ppath, Mountflagsname, Pptr },
    [SYS_unmount]	= { Ppath, Mountflagsname },
    [SYS_setuid]	= { Uidname },
    [SYS_ptrace]	= { Ptracedecode, Ppid_t, Pptr, Pdecint },
    [SYS_recvmsg]	= { Pfd, Pptr, Sendrecvflagsname },
    [SYS_sendmsg]	= { Pfd, Pptr, Sendrecvflagsname },
    [SYS_recvfrom]	= { Pfd, Pptr, Pbigsize, Sendrecvflagsname },
    [SYS_accept]	= { Pfd, Pptr, Pptr },
    [SYS_getpeername]	= { Pfd, Pptr, Pptr },
    [SYS_getsockname]	= { Pfd, Pptr, Pptr },
    [SYS_access]	= { Ppath, Accessmodename },
    [SYS_chflags]	= { Ppath, Chflagsname },
    [SYS_fchflags]	= { Pfd, Chflagsname },
    [SYS_stat]		= { Ppath, Pptr },
    [SYS_lstat]		= { Ppath, Pptr },
    [SYS_dup]		= { Pfd },
    [SYS_fstatat]	= { Atfd, Ppath, Pptr, Atflagsname },
    [SYS_profil]	= { Pptr, Pbigsize, Pbigsize, Pdecuint },
    [SYS_ktrace]	= { Ppath, Ktraceopname, Ktracefacname, Ppgid },
    [SYS_sigaction]	= { Signame, Pptr, Pptr },
    [SYS_sigprocmask]	= { Sigprocmaskhowname, Sigset },
    [SYS_mmap]		= { Pptr, Pbigsize, Mmapprotname, Mmapflagsname, Pfd, Poff_t, END64 },
    [SYS_setlogin]	= { Pptr },
    [SYS_acct]		= { Ppath },
    [SYS_fstat]		= { Pfd, Pptr },
    [SYS_ioctl]		= { Pfd, Ioctldecode, Pptr },
    [SYS_reboot]	= { Rebootoptname },
    [SYS_revoke]	= { Ppath },
    [SYS_symlink]	= { Ppath, Ppath },
    [SYS_readlink]	= { Ppath, Pptr, Psize },
    [SYS_execve]	= { Ppath, Pptr, Pptr },
    [SYS_umask]		= { Modename },
    [SYS_chroot]	= { Ppath },
    [SYS_getfsstat]	= { Pptr, Pbigsize, Mountflagsname },
    [SYS_statfs]	= { Ppath, Pptr },
    [SYS_fstatfs]	= { Pfd, Pptr },
    [SYS_fhstatfs]	= { Pptr, Pptr },
    [SYS_gettimeofday]	= { Pptr, Pptr },
    [SYS_settimeofday]	= { Pptr, Pptr },
    [SYS_setitimer]	= { Itimername, Pptr, Pptr },
    [SYS_getitimer]	= { Itimername, Pptr },
    [SYS_select]	= { Pcount, Pptr, Pptr, Pptr, Pptr },
    [SYS_kevent]	= { Pfd, Pptr, Pcount, Pptr, Pcount, Pptr },
    [SYS_munmap]	= { Pptr, Pbigsize },
    [SYS_mprotect]	= { Pptr, Pbigsize, Mmapprotname },
    [SYS_madvise]	= { Pptr, Pbigsize, Madvisebehavname },
    [SYS_utimes]	= { Ppath, Pptr },
    [SYS_futimes]	= { Pfd, Pptr },
    [SYS_mquery]	= { Pptr, Pbigsize, Mmapprotname, Mmapflagsname, Pfd, Poff_t, END64 },
    [SYS_getgroups]	= { Pcount, Pptr },
    [SYS_setgroups]	= { Pcount, Pptr },
    [SYS_setpgid]	= { Ppid_t, Ppid_t },
    [SYS_futex]		= { Pptr, Futexflagname, Pcount, Pptr, Pptr },
    [SYS_utimensat]	= { Atfd, Ppath, Pptr, Atflagsname },
    [SYS_futimens]	= { Pfd, Pptr },
    [SYS_kbind]		= { Pptr, Psize, Phexlonglong },
    [SYS_clock_gettime]	= { Clockname, Pptr },
    [SYS_clock_settime]	= { Clockname, Pptr },
    [SYS_clock_getres]	= { Clockname, Pptr },
    [SYS_dup2]		= { Pfd, Pfd },
    [SYS_nanosleep]	= { Pptr, Pptr },
    [SYS_fcntl]		= { Pfd, PASS_TWO, Fcntlcmdname },
    [SYS_accept4]	= { Pfd, Pptr, Pptr, Sockflagsname },
    [SYS___thrsleep]	= { Pptr, Clockname, Pptr, Pptr, Pptr },
    [SYS_fsync]		= { Pfd },
    [SYS_setpriority]	= { Prioname, Ppid_t, Pdecint },
    [SYS_socket]	= { Sockfamilyname, Socktypename, Sockprotoname },
    [SYS_connect]	= { Pfd, Pptr, Pucount },
    [SYS_getdents]	= { Pfd, Pptr, Pbigsize },
    [SYS_getpriority]	= { Prioname, Ppid_t },
    [SYS_pipe2]		= { Pptr, Flagsname },
    [SYS_dup3]		= { Pfd, Pfd, Flagsname },
    [SYS_sigreturn]	= { Pptr },
    [SYS_bind]		= { Pfd, Pptr, Pucount },
    [SYS_setsockopt]	= { Pfd, PASS_TWO, Sockoptlevelname, Pptr, Pdecint },
    [SYS_listen]	= { Pfd, Pdecint },
    [SYS_chflagsat]	= { Atfd, Ppath, Chflagsname, Atflagsname },
    [SYS_pledge]	= { Pptr, Pptr },
    [SYS_ppoll]		= { Pptr, Pucount, Pptr, Pptr },
    [SYS_pselect]	= { Pcount, Pptr, Pptr, Pptr, Pptr, Pptr },
    [SYS_sigsuspend]	= { Sigset },
    [SYS_sendsyslog]	= { Pptr, Psize, Syslogflagname },
    [SYS_unveil]	= { Ppath, Pptr },
    [SYS___realpath]	= { Ppath, Pptr },
    [SYS_recvmmsg]	= { Pfd, Pptr, Pucount, Sendrecvflagsname, Pptr },
    [SYS_sendmmsg]	= { Pfd, Pptr, Pucount, Sendrecvflagsname },
    [SYS_getsockopt]	= { Pfd, PASS_TWO, Sockoptlevelname, Pptr, Pptr },
    [SYS_thrkill]	= { Ppid_t, Signame, Pptr },
    [SYS_readv]		= { Pfd, Pptr, Pcount },
    [SYS_writev]	= { Pfd, Pptr, Pcount },
    [SYS_kill]		= { Ppgid, Signame },
    [SYS_fchown]	= { Pfd, Uidname, Gidname },
    [SYS_fchmod]	= { Pfd, Modename },
    [SYS_setreuid]	= { Uidname, Uidname },
    [SYS_setregid]	= { Gidname, Gidname },
    [SYS_rename]	= { Ppath, Ppath },
    [SYS_flock]		= { Pfd, Flockname },
    [SYS_mkfifo]	= { Ppath, Modename },
    [SYS_sendto]	= { Pfd, Pptr, Pbigsize, Sendrecvflagsname },
    [SYS_shutdown]	= { Pfd, Shutdownhowname },
    [SYS_socketpair]	= { Sockfamilyname, Socktypename, Sockprotoname, Pptr },
    [SYS_mkdir]		= { Ppath, Modename },
    [SYS_rmdir]		= { Ppath },
    [SYS_adjtime]	= { Pptr, Pptr },
    [SYS_getlogin_r]	= { Pptr, Psize },
    [SYS_getthrname]	= { Ppid_t, Pptr, Psize },
    [SYS_setthrname]	= { Ppid_t, Pptr },
    [SYS_quotactl]	= { Ppath, Quotactlname, Uidname, Pptr },
    [SYS_ypconnect]	= { Socktypename },
    [SYS_nfssvc]	= { Phexint, Pptr },
    [SYS_mimmutable]	= { Pptr, Pbigsize },
    [SYS_waitid]	= { PASS_TWO, Idtypeandid, Pptr, Waitidoptname },
    [SYS_getfh]		= { Ppath, Pptr },
    [SYS___tmpfd]	= { Openflagsname },
    [SYS_sysarch]	= { Pdecint, Pptr },
    [SYS_lseek]		= { Pfd, Poff_t, Whencename, END64 },
    [SYS_truncate]	= { Ppath, Poff_t, END64 },
    [SYS_ftruncate]	= { Pfd, Poff_t, END64 },
    [SYS_pread]		= { Pfd, Pptr, Pbigsize, Poff_t, END64 },
    [SYS_pwrite]        = { Pfd, Pptr, Pbigsize, Poff_t, END64 },
    [SYS_preadv]	= { Pfd, Pptr, Pcount, Poff_t, END64 },
    [SYS_pwritev]	= { Pfd, Pptr, Pcount, Poff_t, END64 },
    [SYS_setgid]	= { Gidname },
    [SYS_setegid]	= { Gidname },
    [SYS_seteuid]	= { Uidname },
    [SYS_pathconf]	= { Ppath, Pathconfname },
    [SYS_fpathconf]	= { Pfd, Pathconfname },
    [SYS_swapctl]	= { Swapctlname, Pptr, Pdecint },
    [SYS_getrlimit]	= { Rlimitname, Pptr },
    [SYS_setrlimit]	= { Rlimitname, Pptr },
    [SYS_sysctl]	= { Pptr, Pcount, Pptr, Pptr, Pptr, Psize },
    [SYS_mlock]		= { Pptr, Pbigsize },
    [SYS_munlock]	= { Pptr, Pbigsize },
    [SYS_getpgid]	= { Ppid_t },
    [SYS_utrace]	= { Pptr, Pptr, Psize },
    [SYS_semget]	= { Pkey_t, Pcount, Semgetname },
    [SYS_msgget]	= { Pkey_t, Msgflgname },
    [SYS_msgsnd]	= { Pmsqid, Pptr, Psize, Msgflgname },
    [SYS_msgrcv]	= { Pmsqid, Pptr, Psize, Pdeclong, Msgflgname },
    [SYS_shmat]		= { Pshmid, Pptr, Shmatname },
    [SYS_shmdt]		= { Pptr },
    [SYS_minherit]	= { Pptr, Pbigsize, Minheritname },
    [SYS_poll]		= { Pptr, Pucount, Polltimeout },
    [SYS_lchown]	= { Ppath, Uidname, Gidname },
    [SYS_getsid]	= { Ppid_t },
    [SYS_msync]		= { Pptr, Pbigsize, Msyncflagsname },
    [SYS_pipe]		= { Pptr },
    [SYS_fhopen]	= { Pptr, Openflagsname },
    [SYS_kqueue1]	= { Flagsname },
    [SYS_mlockall]	= { Mlockallname },
    [SYS_getresuid]	= { Pptr, Pptr, Pptr },
    [SYS_setresuid]	= { Uidname, Uidname, Uidname },
    [SYS_getresgid]	= { Pptr, Pptr, Pptr },
    [SYS_setresgid]	= { Gidname, Gidname, Gidname },
    [SYS_closefrom]	= { Pfd },
    [SYS_sigaltstack]	= { Pptr, Pptr },
    [SYS_shmget]	= { Pkey_t, Pbigsize, Semgetname },
    [SYS_semop]		= { Psemid, Pptr, Psize },
    [SYS_fhstat]	= { Pptr, Pptr },
    [SYS___semctl]	= { Psemid, Pcount, Semctlname, Pptr },
    [SYS_shmctl]	= { Pshmid, Shmctlname, Pptr },
    [SYS_msgctl]	= { Pmsqid, Shmctlname, Pptr },
    [SYS___thrwakeup]	= { Pptr, Pcount },
    [SYS___threxit]	= { Pptr },
    [SYS___thrsigdivert] = { Sigset, Pptr, Pptr },
    [SYS___getcwd]	= { Pptr, Psize },
    [SYS_adjfreq]	= { Pptr, Pptr },
    [SYS_setrtable]	= { Pdecint },
    [SYS_faccessat]	= { Atfd, Ppath, Accessmodename, Atflagsname },
    [SYS_fchmodat]	= { Atfd, Ppath, Modename, Atflagsname },
    [SYS_fchownat]	= { Atfd, Ppath, Uidname, Gidname, Atflagsname },
    [SYS_linkat]	= { Atfd, Ppath, Atfd, Ppath, Atflagsname },
    [SYS_mkdirat]	= { Atfd, Ppath, Modename },
    [SYS_mkfifoat]	= { Atfd, Ppath, Modename },
    [SYS_mknodat]	= { Atfd, Ppath, Modename, Pdev_t },
    [SYS_openat]	= { Atfd, Ppath, PASS_TWO, Flagsandmodename },
    [SYS_readlinkat]	= { Atfd, Ppath, Pptr, Psize },
    [SYS_renameat]	= { Atfd, Ppath, Atfd, Ppath },
    [SYS_symlinkat]	= { Ppath, Atfd, Ppath },
    [SYS_unlinkat]	= { Atfd, Ppath, Atflagsname },
    [SYS___set_tcb]	= { Pptr },
};


static void
ktrsyscall(struct ktr_syscall *ktr, size_t ktrlen)
{
	register_t *ap;
	int narg, code;
	char sep;

	if (ktr->ktr_argsize > ktrlen)
		errx(1, "syscall argument length %d > ktr header length %zu",
		    ktr->ktr_argsize, ktrlen);

	narg = ktr->ktr_argsize / sizeof(register_t);
	sep = '\0';

	code = ktr->ktr_code;
	if (code >= SYS_MAXSYSCALL || code < 0)
		(void)printf("[%d]", code);
	else
		(void)printf("%s", syscallnames[code]);
	ap = (register_t *)((char *)ktr + sizeof(struct ktr_syscall));
	(void)putchar('(');

	if (code == SYS_sysctl && fancy) {
		const char *s;
		int n, i, *top;

		n = ap[1];
		if (n > CTL_MAXNAME)
			n = CTL_MAXNAME;
		if (n < 0)
			errx(1, "invalid sysctl length %d", n);
		if (n > 0) {
			top = (int *)(ap + 6);
			printf("%d", top[0]);
			for (i = 1; i < n; i++)
				printf(".%d", top[i]);
			if ((s = kresolvsysctl(0, top)) != NULL) {
				printf("<%s", s);
				for (i = 1; i < n; i++) {
					if ((s = kresolvsysctl(i, top)) != NULL)
						printf(".%s", s);
					else
						printf(".%d", top[i]);
				}
				putchar('>');
			}
		}

		sep = ',';
		ap += 2;
		narg -= 2;
	} else if (code < nitems(scargs)) {
		const formatter *fmts = scargs[code];
		int fmt;
		int arg = 0;

		while (arg < narg && (fmt = *fmts) != 0) {
			if (PAD64 && fmt == PASS_LONGLONG && (arg & 1))
				goto skip;
			if (sep)
				putchar(sep);
			sep = ',';
			if (!fancy && !FMT_IS_NONFANCY(fmt))
				fmt = Pnonfancy;
			if (fmt > 0)
				formatters[fmt]((int)*ap);
			else if (long_formatters[-fmt](*ap))
				sep = '\0';
			fmts++;
skip:
			ap++;
			arg++;
		}
		narg -= arg;
	}

	while (narg > 0) {
		if (sep)
			putchar(sep);
		if (decimal)
			(void)printf("%ld", (long)*ap);
		else
			(void)printf("%#lx", (long)*ap);
		sep = ',';
		ap++;
		narg--;
	}
	(void)printf(")\n");
}

static struct ctlname topname[] = CTL_NAMES;
static struct ctlname kernname[] = CTL_KERN_NAMES;
static struct ctlname vmname[] = CTL_VM_NAMES;
static struct ctlname fsname[] = CTL_FS_NAMES;
static struct ctlname netname[] = CTL_NET_NAMES;
static struct ctlname hwname[] = CTL_HW_NAMES;
static struct ctlname debugname[CTL_DEBUG_MAXID];
static struct ctlname kernmallocname[] = CTL_KERN_MALLOC_NAMES;
static struct ctlname forkstatname[] = CTL_KERN_FORKSTAT_NAMES;
static struct ctlname nchstatsname[] = CTL_KERN_NCHSTATS_NAMES;
static struct ctlname kernprocname[] = {
	{ NULL },
	{ "all" },
	{ "pid" },
	{ "pgrp" },
	{ "session" },
	{ "tty" },
	{ "uid" },
	{ "ruid" },
	{ "kthread" },
};
static struct ctlname ttysname[] = CTL_KERN_TTY_NAMES;
static struct ctlname semname[] = CTL_KERN_SEMINFO_NAMES;
static struct ctlname shmname[] = CTL_KERN_SHMINFO_NAMES;
static struct ctlname watchdogname[] = CTL_KERN_WATCHDOG_NAMES;
static struct ctlname tcname[] = CTL_KERN_TIMECOUNTER_NAMES;
#ifdef CTL_MACHDEP_NAMES
static struct ctlname machdepname[] = CTL_MACHDEP_NAMES;
#endif
static struct ctlname ddbname[] = CTL_DDB_NAMES;

#ifndef nitems
#define nitems(_a)    (sizeof((_a)) / sizeof((_a)[0]))
#endif

#define SETNAME(name) do { names = (name); limit = nitems(name); } while (0)

static const char *
kresolvsysctl(int depth, const int *top)
{
	struct ctlname *names;
	size_t		limit;
	int		idx = top[depth];

	names = NULL;

	switch (depth) {
	case 0:
		SETNAME(topname);
		break;
	case 1:
		switch (top[0]) {
		case CTL_KERN:
			SETNAME(kernname);
			break;
		case CTL_VM:
			SETNAME(vmname);
			break;
		case CTL_FS:
			SETNAME(fsname);
			break;
		case CTL_NET:
			SETNAME(netname);
			break;
		case CTL_DEBUG:
			SETNAME(debugname);
			break;
		case CTL_HW:
			SETNAME(hwname);
			break;
#ifdef CTL_MACHDEP_NAMES
		case CTL_MACHDEP:
			SETNAME(machdepname);
			break;
#endif
		case CTL_DDB:
			SETNAME(ddbname);
			break;
		}
		break;
	case 2:
		switch (top[0]) {
		case CTL_KERN:
			switch (top[1]) {
			case KERN_MALLOCSTATS:
				SETNAME(kernmallocname);
				break;
			case KERN_FORKSTAT:
				SETNAME(forkstatname);
				break;
			case KERN_NCHSTATS:
				SETNAME(nchstatsname);
				break;
			case KERN_TTY:
				SETNAME(ttysname);
				break;
			case KERN_SEMINFO:
				SETNAME(semname);
				break;
			case KERN_SHMINFO:
				SETNAME(shmname);
				break;
			case KERN_WATCHDOG:
				SETNAME(watchdogname);
				break;
			case KERN_PROC:
				idx++;	/* zero is valid at this level */
				SETNAME(kernprocname);
				break;
			case KERN_TIMECOUNTER:
				SETNAME(tcname);
				break;
			}
		}
		break;
	}
	if (names != NULL && idx > 0 && idx < limit)
		return (names[idx].ctl_name);
	return (NULL);
}

static void
ktrsysret(struct ktr_sysret *ktr, size_t ktrlen)
{
	register_t ret = 0;
	long long retll;
	int error = ktr->ktr_error;
	int code = ktr->ktr_code;

	if (ktrlen < sizeof(*ktr))
		errx(1, "sysret length %zu < ktr header length %zu",
		    ktrlen, sizeof(*ktr));
	ktrlen -= sizeof(*ktr);
	if (error == 0) {
		if (ktrlen == sizeof(ret)) {
			memcpy(&ret, ktr+1, sizeof(ret));
			retll = ret;
		} else if (ktrlen == sizeof(retll))
			memcpy(&retll, ktr+1, sizeof(retll));
		else
			errx(1, "sysret bogus length %zu", ktrlen);
	}

	if (code >= SYS_MAXSYSCALL || code < 0)
		(void)printf("[%d] ", code);
	else
		(void)printf("%s ", syscallnames[code]);

doerr:
	if (error == 0) {
		if (fancy) {
			switch (code) {
			case SYS_lseek:
				(void)printf("%lld", retll);
				if (retll < 0 || retll > 9)
					(void)printf("/%#llx", retll);
				break;
			case SYS_sigprocmask:
			case SYS_sigpending:
				sigset(ret);
				break;
			case SYS___thrsigdivert:
				signame(ret);
				break;
			case SYS_getuid:
			case SYS_geteuid:
				uidname(ret);
				break;
			case SYS_getgid:
			case SYS_getegid:
				gidname(ret);
				break;
			/* syscalls that return errno values */
			case SYS_getlogin_r:
			case SYS___thrsleep:
			case SYS_getthrname:
			case SYS_setthrname:
				if ((error = ret) != 0)
					goto doerr;
				/* FALLTHROUGH */
			default:
				(void)printf("%ld", (long)ret);
				if (ret < 0 || ret > 9)
					(void)printf("/%#lx", (long)ret);
			}
		} else {
			if (decimal)
				(void)printf("%lld", retll);
			else
				(void)printf("%#llx", retll);
		}
	} else if (error == ERESTART)
		(void)printf("RESTART");
	else if (error == EJUSTRETURN)
		(void)printf("JUSTRETURN");
	else {
		(void)printf("-1 errno %d", error);
		if (fancy)
			(void)printf(" %s", strerror(error));
	}
	(void)putchar('\n');
}

static void
ktrnamei(const char *cp, size_t len)
{
	showbufc(basecol, (unsigned char *)cp, len, VIS_DQ | VIS_TAB | VIS_NL);
}

void
showbufc(int col, unsigned char *dp, size_t datalen, int flags)
{
	int width;
	unsigned char visbuf[5], *cp;

	flags |= VIS_CSTYLE;
	putchar('"');
	col++;
	for (; datalen > 0; datalen--, dp++) {
		(void)vis(visbuf, *dp, flags, *(dp+1));
		cp = visbuf;

		/*
		 * Keep track of printables and
		 * space chars (like fold(1)).
		 */
		if (col == 0) {
			(void)putchar('\t');
			col = 8;
		}
		switch (*cp) {
		case '\n':
			col = 0;
			(void)putchar('\n');
			continue;
		case '\t':
			width = 8 - (col&07);
			break;
		default:
			width = strlen(cp);
		}
		if (col + width > (screenwidth-2)) {
			(void)printf("\\\n\t");
			col = 8;
		}
		col += width;
		do {
			(void)putchar(*cp++);
		} while (*cp);
	}
	if (col == 0)
		(void)printf("       ");
	(void)printf("\"\n");
}

static void
showbuf(unsigned char *dp, size_t datalen)
{
	size_t i, j;
	int col = 0, bpl;
	unsigned char c;
	char visbuf[4 * KTR_USER_MAXLEN + 1];

	if (utracefilter != NULL) {
		strvisx(visbuf, dp, datalen, VIS_SAFE | VIS_OCTAL);
		printf("%s", visbuf);
		return;
	}
	if (iohex == 1) {
		putchar('\t');
		col = 8;
		for (i = 0; i < datalen; i++) {
			printf("%02x", dp[i]);
			col += 3;
			if (i < datalen - 1) {
				if (col + 3 > screenwidth) {
					printf("\n\t");
					col = 8;
				} else
					putchar(' ');
			}
		}
		putchar('\n');
		return;
	}
	if (iohex == 2) {
		bpl = (screenwidth - 13)/4;
		if (bpl <= 0)
			bpl = 1;
		for (i = 0; i < datalen; i += bpl) {
			printf("   %04zx:  ", i);
			for (j = 0; j < bpl; j++) {
				if (i+j >= datalen)
					printf("   ");
				else
					printf("%02x ", dp[i+j]);
			}
			putchar(' ');
			for (j = 0; j < bpl; j++) {
				if (i+j >= datalen)
					break;
				c = dp[i+j];
				if (!isprint(c))
					c = '.';
				putchar(c);
			}
			putchar('\n');
		}
		return;
	}

	(void)printf("       ");
	showbufc(7, dp, datalen, 0);
}

static void
ktrgenio(struct ktr_genio *ktr, size_t len)
{
	unsigned char *dp = (unsigned char *)ktr + sizeof(struct ktr_genio);
	size_t datalen;

	if (len < sizeof(struct ktr_genio))
		errx(1, "invalid ktr genio length %zu", len);

	datalen = len - sizeof(struct ktr_genio);

	printf("fd %d %s %zu bytes\n", ktr->ktr_fd,
		ktr->ktr_rw == UIO_READ ? "read" : "wrote", datalen);
	if (maxdata == 0)
		return;
	if (datalen > maxdata)
		datalen = maxdata;
	if (iohex && !datalen)
		return;
	showbuf(dp, datalen);
}

void
siginfo(const siginfo_t *si, int show_signo)
{
	if (show_signo) {
		printf("signo=");
		signame(si->si_signo);
	}
	if (si->si_code) {
		printf(" code=");
		if (!fancy)
			printf("<%d>", si->si_code);
		else {
			switch (si->si_signo) {
			case SIGILL:
				sigill_name(si->si_code);
				break;
			case SIGTRAP:
				sigtrap_name(si->si_code);
				break;
			case SIGEMT:
				sigemt_name(si->si_code);
				break;
			case SIGFPE:
				sigfpe_name(si->si_code);
				break;
			case SIGBUS:
				sigbus_name(si->si_code);
				break;
			case SIGSEGV:
				sigsegv_name(si->si_code);
				break;
			case SIGCHLD:
				sigchld_name(si->si_code);
				break;
			default:
				printf("<%d>", si->si_code);
				break;
			}
		}
	}

	switch (si->si_signo) {
	case SIGSEGV:
	case SIGILL:
	case SIGBUS:
	case SIGFPE:
		printf(" addr=%p trapno=%d", si->si_addr, si->si_trapno);
		break;
	case SIGCHLD:
		if (si->si_code == CLD_EXITED) {
			printf(" status=%d", si->si_status);
			if (si->si_status < 0 || si->si_status > 9)
				(void)printf("/%#x", si->si_status);
		} else {
			printf(" status=");
			signame(si->si_status);
		}
		printf(" pid=%d uid=", si->si_pid);
		uidname(si->si_uid);
		break;
	default:
		break;
	}
}

static void
ktrpsig(struct ktr_psig *psig)
{
	signame(psig->signo);
	printf(" ");
	if (psig->action == SIG_DFL)
		printf("SIG_DFL");
	else {
		printf("caught handler=0x%lx mask=", (u_long)psig->action);
		sigset(psig->mask);
	}
	siginfo(&psig->si, 0);
	putchar('\n');
}

static void
ktruser(struct ktr_user *usr, size_t len)
{
	if (len < sizeof(struct ktr_user))
		errx(1, "invalid ktr user length %zu", len);
	len -= sizeof(struct ktr_user);
	if (utracefilter == NULL) {
		printf("%.*s:", KTR_USER_MAXIDLEN, usr->ktr_id);
		printf(" %zu bytes\n", len);
		showbuf((unsigned char *)(usr + 1), len);
	} else if (strncmp(usr->ktr_id, utracefilter, KTR_USER_MAXIDLEN) == 0)
		showbuf((unsigned char *)(usr + 1), len);
}

static void
ktrexec(const char *ptr, size_t len)
{
	int i, col;
	size_t l;

	putchar('\n');
	i = 0;
	while (len > 0) {
		l = strnlen(ptr, len);
		col = printf("\t[%d] = ", i++);
		col += 7;	/* tab expands from 1 to 8 columns */
		showbufc(col, (unsigned char *)ptr, l, VIS_DQ|VIS_TAB|VIS_NL);
		if (l == len) {
			printf("\tunterminated argument\n");
			break;
		}
		len -= l + 1;
		ptr += l + 1;
	}
}

static void
ktrpledge(struct ktr_pledge *pledge, size_t len)
{
	const char *name = "";
	int i;

	if (len < sizeof(struct ktr_pledge))
		errx(1, "invalid ktr pledge length %zu", len);

	if (pledge->syscall >= SYS_MAXSYSCALL || pledge->syscall < 0)
		(void)printf("[%d]", pledge->syscall);
	else
		(void)printf("%s", syscallnames[pledge->syscall]);
	printf(", ");
	for (i = 0; pledge->code && pledgenames[i].bits != 0; i++) {
		if (pledgenames[i].bits & pledge->code) {
			name = pledgenames[i].name;
			break;
		}
	}
	printf("\"%s\"", name);
	(void)printf(", errno %d", pledge->error);
	if (fancy)
		(void)printf(" %s", strerror(pledge->error));
	printf("\n");
}

static void
ktrpinsyscall(struct ktr_pinsyscall *pinsyscall, size_t len)
{
	const char *name = "";
	int i;

	if (len < sizeof(struct ktr_pinsyscall))
		errx(1, "invalid ktr pinsyscall length %zu", len);

	if (pinsyscall->syscall >= SYS_MAXSYSCALL || pinsyscall->syscall < 0)
		(void)printf("[%d]", pinsyscall->syscall);
	else
		(void)printf("%s", syscallnames[pinsyscall->syscall]);
	(void)printf(", addr %lx, errno %d", pinsyscall->addr,
	    pinsyscall->error);
	(void)printf(", errno %d", pinsyscall->error);
	if (fancy)
		(void)printf(" %s", strerror(pinsyscall->error));
	printf("\n");
}

static void
usage(void)
{

	extern char *__progname;
	fprintf(stderr, "usage: %s "
	    "[-dHlnRTXx] [-f file] [-m maxdata] [-P program] [-p pid] "
	    "[-t trstr]\n\t[-u label]\n", __progname);
	exit(1);
}


/*
 * FORMATTERS
 */

static void
ioctldecode(int cmd)
{
	char dirbuf[4], *dir = dirbuf;
	const char *cp;

	if ((cp = ioctlname((unsigned)cmd)) != NULL) {
		(void)printf("%s", cp);
		return;
	}

	if (cmd & IOC_IN)
		*dir++ = 'W';
	if (cmd & IOC_OUT)
		*dir++ = 'R';
	*dir = '\0';

	printf("_IO%s('%c',%d",
	    dirbuf, (int)((cmd >> 8) & 0xff), cmd & 0xff);
	if ((cmd & IOC_VOID) == 0)
		printf(decimal ? ",%u)" : ",%#x)", (cmd >> 16) & 0xff);
	else
		printf(")");
}

static void
ptracedecode(int request)
{
	if (request >= 0 && request < nitems(ptrace_ops))
		(void)printf("%s", ptrace_ops[request]);
	else switch(request) {
#ifdef PT_GETFPREGS
	case PT_GETFPREGS:
		(void)printf("PT_GETFPREGS");
		break;
#endif
	case PT_GETREGS:
		(void)printf("PT_GETREGS");
		break;
#ifdef PT_GETXMMREGS
	case PT_GETXMMREGS:
		(void)printf("PT_GETXMMREGS");
		break;
#endif
#ifdef PT_SETFPREGS
	case PT_SETFPREGS:
		(void)printf("PT_SETFPREGS");
		break;
#endif
	case PT_SETREGS:
		(void)printf("PT_SETREGS");
		break;
#ifdef PT_SETXMMREGS
	case PT_SETXMMREGS:
		(void)printf("PT_SETXMMREGS");
		break;
#endif
#ifdef PT_STEP
	case PT_STEP:
		(void)printf("PT_STEP");
		break;
#endif
#ifdef PT_WCOOKIE
	case PT_WCOOKIE:
		(void)printf("PT_WCOOKIE");
		break;
#endif
	default:
		pdecint(request);
	}
}


static void
atfd(int fd)
{
	if (fd == AT_FDCWD)
		(void)printf("AT_FDCWD");
	else
		pdecint(fd);
}

static void
polltimeout(int timeout)
{
	if (timeout == INFTIM)
		(void)printf("INFTIM");
	else
		pdecint(timeout);
}

static void
wait4pid(int pid)
{
	if (pid == WAIT_ANY)
		(void)printf("WAIT_ANY");
	else if (pid == WAIT_MYPGRP)
		(void)printf("WAIT_MYPGRP");
	else
		pdecint(pid);		/* ppgid */
}

static void
signame(int sig)
{
	if (sig > 0 && sig < NSIG)
		(void)printf("SIG%s", sys_signame[sig]);
	else
		(void)printf("SIG %d", sig);
}

void
sigset(int ss)
{
	int	or = 0;
	int	cnt = 0;
	int	i;

	for (i = 1; i < NSIG; i++)
		if (sigismember(&ss, i))
			cnt++;
	if (cnt > (NSIG-1)/2) {
		ss = ~ss;
		putchar('~');
	}

	if (ss == 0) {
		(void)printf("0<>");
		return;
	}

	printf("%#x<", ss);
	for (i = 1; i < NSIG; i++)
		if (sigismember(&ss, i)) {
			if (or) putchar('|'); else or=1;
			signame(i);
		}
	printf(">");
}

static void
semctlname(int cmd)
{
	switch (cmd) {
	case GETNCNT:
		(void)printf("GETNCNT");
		break;
	case GETPID:
		(void)printf("GETPID");
		break;
	case GETVAL:
		(void)printf("GETVAL");
		break;
	case GETALL:
		(void)printf("GETALL");
		break;
	case GETZCNT:
		(void)printf("GETZCNT");
		break;
	case SETVAL:
		(void)printf("SETVAL");
		break;
	case SETALL:
		(void)printf("SETALL");
		break;
	case IPC_RMID:
		(void)printf("IPC_RMID");
		break;
	case IPC_SET:
		(void)printf("IPC_SET");
		break;
	case IPC_STAT:
		(void)printf("IPC_STAT");
		break;
	default: /* Should not reach */
		(void)printf("<invalid=%d>", cmd);
	}
}

static void
shmctlname(int cmd)
{
	switch (cmd) {
	case IPC_RMID:
		(void)printf("IPC_RMID");
		break;
	case IPC_SET:
		(void)printf("IPC_SET");
		break;
	case IPC_STAT:
		(void)printf("IPC_STAT");
		break;
	default: /* Should not reach */
		(void)printf("<invalid=%d>", cmd);
	}
}


static void
semgetname(int flag)
{
	int	or = 0;
	if_print_or(flag, IPC_CREAT, or);
	if_print_or(flag, IPC_EXCL, or);
	if_print_or(flag, SEM_R, or);
	if_print_or(flag, SEM_A, or);
	if_print_or(flag, (SEM_R>>3), or);
	if_print_or(flag, (SEM_A>>3), or);
	if_print_or(flag, (SEM_R>>6), or);
	if_print_or(flag, (SEM_A>>6), or);

	if (flag & ~(IPC_CREAT|IPC_EXCL|SEM_R|SEM_A|((SEM_R|SEM_A)>>3)|
	    ((SEM_R|SEM_A)>>6)))
		printf("<invalid=%#x>", flag);
}


/*
 * Only used by SYS_open and SYS_openat. Unless O_CREAT is set in flags, the
 * mode argument is unused (and often bogus and misleading).
 */
static void
flagsandmodename(int mode)
{
	openflagsname(arg1);
	if ((arg1 & O_CREAT) == O_CREAT) {
		(void)putchar(',');
		modename(mode);
	} else if (!fancy)
		(void)printf(",<unused>%#o", mode);
}

static void
clockname(int clockid)
{
	clocktypename(__CLOCK_TYPE(clockid));
	if (__CLOCK_PTID(clockid) != 0)
		printf("(%d)", __CLOCK_PTID(clockid));
}

/*
 * [g|s]etsockopt's level argument can either be SOL_SOCKET or a value
 * referring to a line in /etc/protocols.
 */
static void
sockoptlevelname(int optname)
{
	struct protoent *pe;

	if (arg1 == SOL_SOCKET) {
		(void)printf("SOL_SOCKET,");
		sockoptname(optname);
	} else {
		pe = getprotobynumber(arg1);
		(void)printf("%u<%s>,%d", arg1,
		    pe != NULL ? pe->p_name : "unknown", optname);
	}
}

static void
ktraceopname(int ops)
{
	int invalid = 0;

	printf("%#x<", ops);
	switch (KTROP(ops)) {
	case KTROP_SET:
		printf("KTROP_SET");
		break;
	case KTROP_CLEAR:
		printf("KTROP_CLEAR");
		break;
	case KTROP_CLEARFILE:
		printf("KTROP_CLEARFILE");
		break;
	default:
		printf("KTROP(%d)", KTROP(ops));
		invalid = 1;
		break;
	}
	if (ops & KTRFLAG_DESCEND) printf("|KTRFLAG_DESCEND");
	printf(">");
	if (invalid || (ops & ~(KTROP((unsigned)-1) | KTRFLAG_DESCEND)))
		(void)printf("<invalid>%d", ops);
}

static void
idtypeandid(int id)
{
	switch (arg1) {
	case P_PID:
		printf("P_PID,%d", id);
		break;
	case P_PGID:
		printf("P_PGID,%d", id);
		break;
	case P_ALL:
		printf("P_ALL,<unused>%d", id);
		break;
	default: /* Should not reach */
		printf("<invalid=%d>, <unused>%d", arg1, id);
	}
}