1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-14 10:09:48 +00:00
freebsd/usr.bin/kdump/kdump.c
Dag-Erling Smørgrav 60e15db992 This patch adds a new ktrace(2) record type, KTR_STRUCT, whose payload
consists of the null-terminated name and the contents of any structure
you wish to record.  A new ktrstruct() function constructs and emits a
KTR_STRUCT record.  It is accompanied by convenience macros for struct
stat and struct sockaddr.

In kdump(1), KTR_STRUCT records are handled by a dispatcher function
that runs stringent sanity checks on its contents before handing it
over to individual decoding funtions for each type of structure.
Currently supported structures are struct stat and struct sockaddr for
the AF_INET, AF_INET6 and AF_UNIX families; support for AF_APPLETALK
and AF_IPX is present but disabled, as I am unable to test it properly.

Since 's' was already taken, the letter 't' is used by ktrace(1) to
enable KTR_STRUCT trace points, and in kdump(1) to enable their
decoding.

Derived from patches by Andrew Li <andrew2.li@citi.com>.

PR:		kern/117836
MFC after:	3 weeks
2008-02-23 01:01:49 +00:00

1373 lines
32 KiB
C

/*-
* 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. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. 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.
*/
#ifndef lint
static const char copyright[] =
"@(#) Copyright (c) 1988, 1993\n\
The Regents of the University of California. All rights reserved.\n";
#endif /* not lint */
#ifndef lint
#if 0
static char sccsid[] = "@(#)kdump.c 8.1 (Berkeley) 6/6/93";
#endif
#endif /* not lint */
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#define _KERNEL
extern int errno;
#include <sys/errno.h>
#undef _KERNEL
#include <sys/param.h>
#include <sys/errno.h>
#define _KERNEL
#include <sys/time.h>
#undef _KERNEL
#include <sys/uio.h>
#include <sys/ktrace.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/un.h>
#ifdef IPX
#include <sys/types.h>
#include <netipx/ipx.h>
#endif
#ifdef NETATALK
#include <netatalk/at.h>
#endif
#include <netinet/in.h>
#include <dlfcn.h>
#include <err.h>
#include <grp.h>
#include <inttypes.h>
#include <locale.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <vis.h>
#include "ktrace.h"
#include "kdump_subr.h"
int fread_tail(void *, int, int);
void dumpheader(struct ktr_header *);
void ktrsyscall(struct ktr_syscall *);
void ktrsysret(struct ktr_sysret *);
void ktrnamei(char *, int);
void hexdump(char *, int, int);
void visdump(char *, int, int);
void ktrgenio(struct ktr_genio *, int);
void ktrpsig(struct ktr_psig *);
void ktrcsw(struct ktr_csw *);
void ktruser(int, unsigned char *);
void ktrsockaddr(struct sockaddr *);
void ktrstat(struct stat *);
void ktrstruct(char *, size_t);
void usage(void);
const char *ioctlname(u_long);
int timestamp, decimal, fancy = 1, suppressdata, tail, threads, maxdata,
resolv = 0;
const char *tracefile = DEF_TRACEFILE;
struct ktr_header ktr_header;
#define TIME_FORMAT "%b %e %T %Y"
#define eqs(s1, s2) (strcmp((s1), (s2)) == 0)
int
main(int argc, char *argv[])
{
int ch, ktrlen, size;
void *m;
int trpoints = ALL_POINTS;
int drop_logged;
pid_t pid = 0;
(void) setlocale(LC_CTYPE, "");
while ((ch = getopt(argc,argv,"f:dElm:np:HRrsTt:")) != -1)
switch((char)ch) {
case 'f':
tracefile = optarg;
break;
case 'd':
decimal = 1;
break;
case 'l':
tail = 1;
break;
case 'm':
maxdata = atoi(optarg);
break;
case 'n':
fancy = 0;
break;
case 'p':
pid = atoi(optarg);
break;
case 'r':
resolv = 1;
break;
case 's':
suppressdata = 1;
break;
case 'E':
timestamp = 3; /* elapsed timestamp */
break;
case 'H':
threads = 1;
break;
case 'R':
timestamp = 2; /* relative timestamp */
break;
case 'T':
timestamp = 1;
break;
case 't':
trpoints = getpoints(optarg);
if (trpoints < 0)
errx(1, "unknown trace point in %s", optarg);
break;
default:
usage();
}
if (argc > optind)
usage();
m = (void *)malloc(size = 1025);
if (m == NULL)
errx(1, "%s", strerror(ENOMEM));
if (!freopen(tracefile, "r", stdin))
err(1, "%s", tracefile);
drop_logged = 0;
while (fread_tail(&ktr_header, sizeof(struct ktr_header), 1)) {
if (ktr_header.ktr_type & KTR_DROP) {
ktr_header.ktr_type &= ~KTR_DROP;
if (!drop_logged && threads) {
(void)printf("%6d %6d %-8.*s Events dropped.\n",
ktr_header.ktr_pid, ktr_header.ktr_tid >
0 ? ktr_header.ktr_tid : 0, MAXCOMLEN,
ktr_header.ktr_comm);
drop_logged = 1;
} else if (!drop_logged) {
(void)printf("%6d %-8.*s Events dropped.\n",
ktr_header.ktr_pid, MAXCOMLEN,
ktr_header.ktr_comm);
drop_logged = 1;
}
}
if (trpoints & (1<<ktr_header.ktr_type))
if (pid == 0 || ktr_header.ktr_pid == pid)
dumpheader(&ktr_header);
if ((ktrlen = ktr_header.ktr_len) < 0)
errx(1, "bogus length 0x%x", ktrlen);
if (ktrlen > size) {
m = (void *)realloc(m, ktrlen+1);
if (m == NULL)
errx(1, "%s", strerror(ENOMEM));
size = ktrlen;
}
if (ktrlen && fread_tail(m, ktrlen, 1) == 0)
errx(1, "data too short");
if (pid && ktr_header.ktr_pid != pid)
continue;
if ((trpoints & (1<<ktr_header.ktr_type)) == 0)
continue;
drop_logged = 0;
switch (ktr_header.ktr_type) {
case KTR_SYSCALL:
ktrsyscall((struct ktr_syscall *)m);
break;
case KTR_SYSRET:
ktrsysret((struct ktr_sysret *)m);
break;
case KTR_NAMEI:
ktrnamei(m, ktrlen);
break;
case KTR_GENIO:
ktrgenio((struct ktr_genio *)m, ktrlen);
break;
case KTR_PSIG:
ktrpsig((struct ktr_psig *)m);
break;
case KTR_CSW:
ktrcsw((struct ktr_csw *)m);
break;
case KTR_USER:
ktruser(ktrlen, m);
break;
case KTR_STRUCT:
ktrstruct(m, ktrlen);
break;
default:
printf("\n");
break;
}
if (tail)
(void)fflush(stdout);
}
return 0;
}
int
fread_tail(void *buf, int size, int num)
{
int i;
while ((i = fread(buf, size, num, stdin)) == 0 && tail) {
(void)sleep(1);
clearerr(stdin);
}
return (i);
}
void
dumpheader(struct ktr_header *kth)
{
static char unknown[64];
static struct timeval prevtime, temp;
const char *type;
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_CSW:
type = "CSW ";
break;
case KTR_USER:
type = "USER";
break;
case KTR_STRUCT:
type = "STRU";
break;
default:
(void)sprintf(unknown, "UNKNOWN(%d)", kth->ktr_type);
type = unknown;
}
/*
* The ktr_tid field was previously the ktr_buffer field, which held
* the kernel pointer value for the buffer associated with data
* following the record header. It now holds a threadid, but only
* for trace files after the change. Older trace files still contain
* kernel pointers. Detect this and suppress the results by printing
* negative tid's as 0.
*/
if (threads)
(void)printf("%6d %6d %-8.*s ", kth->ktr_pid, kth->ktr_tid >
0 ? kth->ktr_tid : 0, MAXCOMLEN, kth->ktr_comm);
else
(void)printf("%6d %-8.*s ", kth->ktr_pid, MAXCOMLEN,
kth->ktr_comm);
if (timestamp) {
if (timestamp == 3) {
if (prevtime.tv_sec == 0)
prevtime = kth->ktr_time;
timevalsub(&kth->ktr_time, &prevtime);
}
if (timestamp == 2) {
temp = kth->ktr_time;
timevalsub(&kth->ktr_time, &prevtime);
prevtime = temp;
}
(void)printf("%ld.%06ld ",
kth->ktr_time.tv_sec, kth->ktr_time.tv_usec);
}
(void)printf("%s ", type);
}
#include <sys/syscall.h>
#define KTRACE
#include <sys/kern/syscalls.c>
#undef KTRACE
int nsyscalls = sizeof (syscallnames) / sizeof (syscallnames[0]);
void
ktrsyscall(struct ktr_syscall *ktr)
{
int narg = ktr->ktr_narg;
register_t *ip;
if (ktr->ktr_code >= nsyscalls || ktr->ktr_code < 0)
(void)printf("[%d]", ktr->ktr_code);
else
(void)printf("%s", syscallnames[ktr->ktr_code]);
ip = &ktr->ktr_args[0];
if (narg) {
char c = '(';
if (fancy) {
#define print_number(i,n,c) do { \
if (decimal) \
(void)printf("%c%ld", c, (long)*i); \
else \
(void)printf("%c%#lx", c, (long)*i); \
i++; \
n--; \
c = ','; \
} while (0);
if (ktr->ktr_code == SYS_ioctl) {
const char *cp;
print_number(ip,narg,c);
if ((cp = ioctlname(*ip)) != NULL)
(void)printf(",%s", cp);
else {
if (decimal)
(void)printf(",%ld", (long)*ip);
else
(void)printf(",%#lx ", (long)*ip);
}
c = ',';
ip++;
narg--;
} else if (ktr->ktr_code == SYS_ptrace) {
(void)putchar('(');
ptraceopname ((int)*ip);
c = ',';
ip++;
narg--;
} else if (ktr->ktr_code == SYS_access ||
ktr->ktr_code == SYS_eaccess) {
print_number(ip,narg,c);
(void)putchar(',');
accessmodename ((int)*ip);
ip++;
narg--;
} else if (ktr->ktr_code == SYS_open) {
int flags;
int mode;
print_number(ip,narg,c);
flags = *ip;
mode = *++ip;
(void)putchar(',');
flagsandmodename (flags, mode, decimal);
ip++;
narg-=2;
} else if (ktr->ktr_code == SYS_wait4) {
print_number(ip,narg,c);
print_number(ip,narg,c);
(void)putchar(',');
wait4optname ((int)*ip);
ip++;
narg--;
} else if (ktr->ktr_code == SYS_chmod ||
ktr->ktr_code == SYS_fchmod ||
ktr->ktr_code == SYS_lchmod) {
print_number(ip,narg,c);
(void)putchar(',');
modename ((int)*ip);
ip++;
narg--;
} else if (ktr->ktr_code == SYS_mknod) {
print_number(ip,narg,c);
(void)putchar(',');
modename ((int)*ip);
ip++;
narg--;
} else if (ktr->ktr_code == SYS_getfsstat) {
print_number(ip,narg,c);
print_number(ip,narg,c);
(void)putchar(',');
getfsstatflagsname ((int)*ip);
ip++;
narg--;
} else if (ktr->ktr_code == SYS_mount) {
print_number(ip,narg,c);
print_number(ip,narg,c);
(void)putchar(',');
mountflagsname ((int)*ip);
ip++;
narg--;
} else if (ktr->ktr_code == SYS_unmount) {
print_number(ip,narg,c);
(void)putchar(',');
mountflagsname ((int)*ip);
ip++;
narg--;
} else if (ktr->ktr_code == SYS_recvmsg ||
ktr->ktr_code == SYS_sendmsg) {
print_number(ip,narg,c);
print_number(ip,narg,c);
(void)putchar(',');
sendrecvflagsname ((int)*ip);
ip++;
narg--;
} else if (ktr->ktr_code == SYS_recvfrom ||
ktr->ktr_code == SYS_sendto) {
print_number(ip,narg,c);
print_number(ip,narg,c);
print_number(ip,narg,c);
(void)putchar(',');
sendrecvflagsname ((int)*ip);
ip++;
narg--;
} else if (ktr->ktr_code == SYS_chflags ||
ktr->ktr_code == SYS_fchflags ||
ktr->ktr_code == SYS_lchflags) {
print_number(ip,narg,c);
(void)putchar(',');
modename((int)*ip);
ip++;
narg--;
} else if (ktr->ktr_code == SYS_kill) {
print_number(ip,narg,c);
(void)putchar(',');
signame((int)*ip);
ip++;
narg--;
} else if (ktr->ktr_code == SYS_reboot) {
(void)putchar('(');
rebootoptname((int)*ip);
ip++;
narg--;
} else if (ktr->ktr_code == SYS_umask) {
(void)putchar('(');
modename((int)*ip);
ip++;
narg--;
} else if (ktr->ktr_code == SYS_msync) {
print_number(ip,narg,c);
print_number(ip,narg,c);
(void)putchar(',');
msyncflagsname((int)*ip);
ip++;
narg--;
#ifdef SYS_freebsd6_mmap
} else if (ktr->ktr_code == SYS_freebsd6_mmap) {
print_number(ip,narg,c);
print_number(ip,narg,c);
(void)putchar(',');
mmapprotname ((int)*ip);
(void)putchar(',');
ip++;
narg--;
mmapflagsname ((int)*ip);
ip++;
narg--;
#endif
} else if (ktr->ktr_code == SYS_mmap) {
print_number(ip,narg,c);
print_number(ip,narg,c);
(void)putchar(',');
mmapprotname ((int)*ip);
(void)putchar(',');
ip++;
narg--;
mmapflagsname ((int)*ip);
ip++;
narg--;
} else if (ktr->ktr_code == SYS_mprotect) {
print_number(ip,narg,c);
print_number(ip,narg,c);
(void)putchar(',');
mmapprotname ((int)*ip);
ip++;
narg--;
} else if (ktr->ktr_code == SYS_madvise) {
print_number(ip,narg,c);
print_number(ip,narg,c);
(void)putchar(',');
madvisebehavname((int)*ip);
ip++;
narg--;
} else if (ktr->ktr_code == SYS_setpriority) {
print_number(ip,narg,c);
print_number(ip,narg,c);
(void)putchar(',');
prioname((int)*ip);
ip++;
narg--;
} else if (ktr->ktr_code == SYS_fcntl) {
int cmd;
int arg;
print_number(ip,narg,c);
cmd = *ip;
arg = *++ip;
(void)putchar(',');
fcntlcmdname(cmd, arg, decimal);
ip++;
narg-=2;
} else if (ktr->ktr_code == SYS_socket) {
int sockdomain;
(void)putchar('(');
sockdomain=(int)*ip;
sockdomainname(sockdomain);
ip++;
narg--;
(void)putchar(',');
socktypename((int)*ip);
ip++;
narg--;
if (sockdomain == PF_INET ||
sockdomain == PF_INET6) {
(void)putchar(',');
sockipprotoname((int)*ip);
ip++;
narg--;
}
c = ',';
} else if (ktr->ktr_code == SYS_setsockopt ||
ktr->ktr_code == SYS_getsockopt) {
print_number(ip,narg,c);
(void)putchar(',');
sockoptlevelname((int)*ip, decimal);
if ((int)*ip == SOL_SOCKET) {
ip++;
narg--;
(void)putchar(',');
sockoptname((int)*ip);
}
ip++;
narg--;
#ifdef SYS_freebsd6_lseek
} else if (ktr->ktr_code == SYS_freebsd6_lseek) {
print_number(ip,narg,c);
/* Hidden 'pad' argument, not in lseek(2) */
print_number(ip,narg,c);
print_number(ip,narg,c);
(void)putchar(',');
whencename ((int)*ip);
ip++;
narg--;
#endif
} else if (ktr->ktr_code == SYS_lseek) {
print_number(ip,narg,c);
/* Hidden 'pad' argument, not in lseek(2) */
print_number(ip,narg,c);
(void)putchar(',');
whencename ((int)*ip);
ip++;
narg--;
} else if (ktr->ktr_code == SYS_flock) {
print_number(ip,narg,c);
(void)putchar(',');
flockname((int)*ip);
ip++;
narg--;
} else if (ktr->ktr_code == SYS_mkfifo ||
ktr->ktr_code == SYS_mkdir) {
print_number(ip,narg,c);
(void)putchar(',');
modename((int)*ip);
ip++;
narg--;
} else if (ktr->ktr_code == SYS_shutdown) {
print_number(ip,narg,c);
(void)putchar(',');
shutdownhowname((int)*ip);
ip++;
narg--;
} else if (ktr->ktr_code == SYS_socketpair) {
(void)putchar('(');
sockdomainname((int)*ip);
ip++;
narg--;
(void)putchar(',');
socktypename((int)*ip);
ip++;
narg--;
c = ',';
} else if (ktr->ktr_code == SYS_getrlimit ||
ktr->ktr_code == SYS_setrlimit) {
(void)putchar('(');
rlimitname((int)*ip);
ip++;
narg--;
c = ',';
} else if (ktr->ktr_code == SYS_quotactl) {
print_number(ip,narg,c);
(void)putchar(',');
quotactlname((int)*ip);
ip++;
narg--;
c = ',';
} else if (ktr->ktr_code == SYS_nfssvc) {
(void)putchar('(');
nfssvcname((int)*ip);
ip++;
narg--;
c = ',';
} else if (ktr->ktr_code == SYS_rtprio) {
(void)putchar('(');
rtprioname((int)*ip);
ip++;
narg--;
c = ',';
} else if (ktr->ktr_code == SYS___semctl) {
print_number(ip,narg,c);
print_number(ip,narg,c);
(void)putchar(',');
semctlname((int)*ip);
ip++;
narg--;
} else if (ktr->ktr_code == SYS_semget) {
print_number(ip,narg,c);
print_number(ip,narg,c);
(void)putchar(',');
semgetname((int)*ip);
ip++;
narg--;
} else if (ktr->ktr_code == SYS_msgctl) {
print_number(ip,narg,c);
(void)putchar(',');
shmctlname((int)*ip);
ip++;
narg--;
} else if (ktr->ktr_code == SYS_shmat) {
print_number(ip,narg,c);
print_number(ip,narg,c);
(void)putchar(',');
shmatname((int)*ip);
ip++;
narg--;
} else if (ktr->ktr_code == SYS_shmctl) {
print_number(ip,narg,c);
(void)putchar(',');
shmctlname((int)*ip);
ip++;
narg--;
} else if (ktr->ktr_code == SYS_minherit) {
print_number(ip,narg,c);
print_number(ip,narg,c);
(void)putchar(',');
minheritname((int)*ip);
ip++;
narg--;
} else if (ktr->ktr_code == SYS_rfork) {
(void)putchar('(');
rforkname((int)*ip);
ip++;
narg--;
c = ',';
} else if (ktr->ktr_code == SYS_lio_listio) {
(void)putchar('(');
lio_listioname((int)*ip);
ip++;
narg--;
c = ',';
} else if (ktr->ktr_code == SYS_mlockall) {
(void)putchar('(');
mlockallname((int)*ip);
ip++;
narg--;
} else if (ktr->ktr_code == SYS_sched_setscheduler) {
print_number(ip,narg,c);
(void)putchar(',');
schedpolicyname((int)*ip);
ip++;
narg--;
} else if (ktr->ktr_code == SYS_sched_get_priority_max ||
ktr->ktr_code == SYS_sched_get_priority_min) {
(void)putchar('(');
schedpolicyname((int)*ip);
ip++;
narg--;
} else if (ktr->ktr_code == SYS_sendfile) {
print_number(ip,narg,c);
print_number(ip,narg,c);
print_number(ip,narg,c);
print_number(ip,narg,c);
print_number(ip,narg,c);
print_number(ip,narg,c);
(void)putchar(',');
sendfileflagsname((int)*ip);
ip++;
narg--;
} else if (ktr->ktr_code == SYS_kldsym) {
print_number(ip,narg,c);
(void)putchar(',');
kldsymcmdname((int)*ip);
ip++;
narg--;
} else if (ktr->ktr_code == SYS_sigprocmask) {
(void)putchar('(');
sigprocmaskhowname((int)*ip);
ip++;
narg--;
c = ',';
} else if (ktr->ktr_code == SYS___acl_get_file ||
ktr->ktr_code == SYS___acl_set_file ||
ktr->ktr_code == SYS___acl_get_fd ||
ktr->ktr_code == SYS___acl_set_fd ||
ktr->ktr_code == SYS___acl_delete_file ||
ktr->ktr_code == SYS___acl_delete_fd ||
ktr->ktr_code == SYS___acl_aclcheck_file ||
ktr->ktr_code == SYS___acl_aclcheck_fd ||
ktr->ktr_code == SYS___acl_get_link ||
ktr->ktr_code == SYS___acl_set_link ||
ktr->ktr_code == SYS___acl_delete_link ||
ktr->ktr_code == SYS___acl_aclcheck_link) {
print_number(ip,narg,c);
(void)putchar(',');
acltypename((int)*ip);
ip++;
narg--;
} else if (ktr->ktr_code == SYS_sigaction) {
(void)putchar('(');
signame((int)*ip);
ip++;
narg--;
c = ',';
} else if (ktr->ktr_code == SYS_extattrctl) {
print_number(ip,narg,c);
(void)putchar(',');
extattrctlname((int)*ip);
ip++;
narg--;
} else if (ktr->ktr_code == SYS_nmount) {
print_number(ip,narg,c);
print_number(ip,narg,c);
(void)putchar(',');
mountflagsname ((int)*ip);
ip++;
narg--;
} else if (ktr->ktr_code == SYS_kse_thr_interrupt) {
print_number(ip,narg,c);
(void)putchar(',');
ksethrcmdname ((int)*ip);
ip++;
narg--;
} else if (ktr->ktr_code == SYS_thr_create) {
print_number(ip,narg,c);
print_number(ip,narg,c);
(void)putchar(',');
thrcreateflagsname ((int)*ip);
ip++;
narg--;
} else if (ktr->ktr_code == SYS_thr_kill) {
print_number(ip,narg,c);
(void)putchar(',');
signame ((int)*ip);
ip++;
narg--;
} else if (ktr->ktr_code == SYS_kldunloadf) {
print_number(ip,narg,c);
(void)putchar(',');
kldunloadfflagsname ((int)*ip);
ip++;
narg--;
}
}
while (narg) {
print_number(ip,narg,c);
}
(void)putchar(')');
}
(void)putchar('\n');
}
void
ktrsysret(struct ktr_sysret *ktr)
{
register_t ret = ktr->ktr_retval;
int error = ktr->ktr_error;
int code = ktr->ktr_code;
if (code >= nsyscalls || code < 0)
(void)printf("[%d] ", code);
else
(void)printf("%s ", syscallnames[code]);
if (error == 0) {
if (fancy) {
(void)printf("%d", ret);
if (ret < 0 || ret > 9)
(void)printf("/%#lx", (long)ret);
} else {
if (decimal)
(void)printf("%ld", (long)ret);
else
(void)printf("%#lx", (long)ret);
}
} else if (error == ERESTART)
(void)printf("RESTART");
else if (error == EJUSTRETURN)
(void)printf("JUSTRETURN");
else {
(void)printf("-1 errno %d", ktr->ktr_error);
if (fancy)
(void)printf(" %s", strerror(ktr->ktr_error));
}
(void)putchar('\n');
}
void
ktrnamei(char *cp, int len)
{
(void)printf("\"%.*s\"\n", len, cp);
}
void
hexdump(char *p, int len, int screenwidth)
{
int n, i;
int width;
width = 0;
do {
width += 2;
i = 13; /* base offset */
i += (width / 2) + 1; /* spaces every second byte */
i += (width * 2); /* width of bytes */
i += 3; /* " |" */
i += width; /* each byte */
i += 1; /* "|" */
} while (i < screenwidth);
width -= 2;
for (n = 0; n < len; n += width) {
for (i = n; i < n + width; i++) {
if ((i % width) == 0) { /* beginning of line */
printf(" 0x%04x", i);
}
if ((i % 2) == 0) {
printf(" ");
}
if (i < len)
printf("%02x", p[i] & 0xff);
else
printf(" ");
}
printf(" |");
for (i = n; i < n + width; i++) {
if (i >= len)
break;
if (p[i] >= ' ' && p[i] <= '~')
printf("%c", p[i]);
else
printf(".");
}
printf("|\n");
}
if ((i % width) != 0)
printf("\n");
}
void
visdump(char *dp, int datalen, int screenwidth)
{
int col = 0;
char *cp;
int width;
char visbuf[5];
(void)printf(" \"");
col = 8;
for (;datalen > 0; datalen--, dp++) {
(void) vis(visbuf, *dp, VIS_CSTYLE, *(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");
}
void
ktrgenio(struct ktr_genio *ktr, int len)
{
int datalen = len - sizeof (struct ktr_genio);
char *dp = (char *)ktr + sizeof (struct ktr_genio);
static int screenwidth = 0;
int i, binary;
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;
}
printf("fd %d %s %d byte%s\n", ktr->ktr_fd,
ktr->ktr_rw == UIO_READ ? "read" : "wrote", datalen,
datalen == 1 ? "" : "s");
if (suppressdata)
return;
if (maxdata && datalen > maxdata)
datalen = maxdata;
for (i = 0, binary = 0; i < datalen && binary == 0; i++) {
if (dp[i] >= 32 && dp[i] < 127)
continue;
if (dp[i] == 10 || dp[i] == 13 || dp[i] == 0 || dp[i] == 9)
continue;
binary = 1;
}
if (binary)
hexdump(dp, datalen, screenwidth);
else
visdump(dp, datalen, screenwidth);
}
const char *signames[] = {
"NULL", "HUP", "INT", "QUIT", "ILL", "TRAP", "IOT", /* 1 - 6 */
"EMT", "FPE", "KILL", "BUS", "SEGV", "SYS", /* 7 - 12 */
"PIPE", "ALRM", "TERM", "URG", "STOP", "TSTP", /* 13 - 18 */
"CONT", "CHLD", "TTIN", "TTOU", "IO", "XCPU", /* 19 - 24 */
"XFSZ", "VTALRM", "PROF", "WINCH", "29", "USR1", /* 25 - 30 */
"USR2", NULL, /* 31 - 32 */
};
void
ktrpsig(struct ktr_psig *psig)
{
if (psig->signo > 0 && psig->signo < NSIG)
(void)printf("SIG%s ", signames[psig->signo]);
else
(void)printf("SIG %d ", psig->signo);
if (psig->action == SIG_DFL)
(void)printf("SIG_DFL\n");
else {
(void)printf("caught handler=0x%lx mask=0x%x code=0x%x\n",
(u_long)psig->action, psig->mask.__bits[0], psig->code);
}
}
void
ktrcsw(struct ktr_csw *cs)
{
(void)printf("%s %s\n", cs->out ? "stop" : "resume",
cs->user ? "user" : "kernel");
}
#define UTRACE_DLOPEN_START 1
#define UTRACE_DLOPEN_STOP 2
#define UTRACE_DLCLOSE_START 3
#define UTRACE_DLCLOSE_STOP 4
#define UTRACE_LOAD_OBJECT 5
#define UTRACE_UNLOAD_OBJECT 6
#define UTRACE_ADD_RUNDEP 7
#define UTRACE_PRELOAD_FINISHED 8
#define UTRACE_INIT_CALL 9
#define UTRACE_FINI_CALL 10
struct utrace_rtld {
char sig[4]; /* 'RTLD' */
int event;
void *handle;
void *mapbase;
size_t mapsize;
int refcnt;
char name[MAXPATHLEN];
};
void
ktruser_rtld(int len, unsigned char *p)
{
struct utrace_rtld *ut = (struct utrace_rtld *)p;
void *parent;
int mode;
switch (ut->event) {
case UTRACE_DLOPEN_START:
mode = ut->refcnt;
printf("dlopen(%s, ", ut->name);
switch (mode & RTLD_MODEMASK) {
case RTLD_NOW:
printf("RTLD_NOW");
break;
case RTLD_LAZY:
printf("RTLD_LAZY");
break;
default:
printf("%#x", mode & RTLD_MODEMASK);
}
if (mode & RTLD_GLOBAL)
printf(" | RTLD_GLOBAL");
if (mode & RTLD_TRACE)
printf(" | RTLD_TRACE");
if (mode & ~(RTLD_MODEMASK | RTLD_GLOBAL | RTLD_TRACE))
printf(" | %#x", mode &
~(RTLD_MODEMASK | RTLD_GLOBAL | RTLD_TRACE));
printf(")\n");
break;
case UTRACE_DLOPEN_STOP:
printf("%p = dlopen(%s) ref %d\n", ut->handle, ut->name,
ut->refcnt);
break;
case UTRACE_DLCLOSE_START:
printf("dlclose(%p) (%s, %d)\n", ut->handle, ut->name,
ut->refcnt);
break;
case UTRACE_DLCLOSE_STOP:
printf("dlclose(%p) finished\n", ut->handle);
break;
case UTRACE_LOAD_OBJECT:
printf("RTLD: loaded %p @ %p - %p (%s)\n", ut->handle,
ut->mapbase, (char *)ut->mapbase + ut->mapsize - 1,
ut->name);
break;
case UTRACE_UNLOAD_OBJECT:
printf("RTLD: unloaded %p @ %p - %p (%s)\n", ut->handle,
ut->mapbase, (char *)ut->mapbase + ut->mapsize - 1,
ut->name);
break;
case UTRACE_ADD_RUNDEP:
parent = ut->mapbase;
printf("RTLD: %p now depends on %p (%s, %d)\n", parent,
ut->handle, ut->name, ut->refcnt);
break;
case UTRACE_PRELOAD_FINISHED:
printf("RTLD: LD_PRELOAD finished\n");
break;
case UTRACE_INIT_CALL:
printf("RTLD: init %p for %p (%s)\n", ut->mapbase, ut->handle,
ut->name);
break;
case UTRACE_FINI_CALL:
printf("RTLD: fini %p for %p (%s)\n", ut->mapbase, ut->handle,
ut->name);
break;
default:
p += 4;
len -= 4;
printf("RTLD: %d ", len);
while (len--)
if (decimal)
printf(" %d", *p++);
else
printf(" %02x", *p++);
printf("\n");
}
}
struct utrace_malloc {
void *p;
size_t s;
void *r;
};
void
ktruser_malloc(int len, unsigned char *p)
{
struct utrace_malloc *ut = (struct utrace_malloc *)p;
if (ut->p == NULL) {
if (ut->s == 0 && ut->r == NULL)
printf("malloc_init()\n");
else
printf("%p = malloc(%zu)\n", ut->r, ut->s);
} else {
if (ut->s == 0)
printf("free(%p)\n", ut->p);
else
printf("%p = realloc(%p, %zu)\n", ut->r, ut->p, ut->s);
}
}
void
ktruser(int len, unsigned char *p)
{
if (len >= 8 && bcmp(p, "RTLD", 4) == 0) {
ktruser_rtld(len, p);
return;
}
if (len == sizeof(struct utrace_malloc)) {
ktruser_malloc(len, p);
return;
}
(void)printf("%d ", len);
while (len--)
if (decimal)
(void)printf(" %d", *p++);
else
(void)printf(" %02x", *p++);
(void)printf("\n");
}
void
ktrsockaddr(struct sockaddr *sa)
{
/*
TODO: Support additional address families
#include <netatm/atm.h>
struct sockaddr_atm *atm;
#include <netnatm/natm.h>
struct sockaddr_natm *natm;
#include <netsmb/netbios.h>
struct sockaddr_nb *nb;
*/
char addr[64];
/*
* note: ktrstruct() has already verified that sa points to a
* buffer at least sizeof(struct sockaddr) bytes long and exactly
* sa->sa_len bytes long.
*/
printf("struct sockaddr { ");
sockfamilyname(sa->sa_family);
printf(", ");
#define check_sockaddr_len(n) \
if (sa_##n->s##n##_len < sizeof(struct sockaddr_##n)) { \
printf("invalid"); \
break; \
}
switch(sa->sa_family) {
case AF_INET: {
struct sockaddr_in *sa_in;
sa_in = (struct sockaddr_in *)sa;
check_sockaddr_len(in);
inet_ntop(AF_INET, &sa_in->sin_addr, addr, sizeof addr);
printf("%s:%u", addr, ntohs(sa_in->sin_port));
break;
}
#ifdef NETATALK
case AF_APPLETALK: {
struct sockaddr_at *sa_at;
struct netrange *nr;
sa_at = (struct sockaddr_at *)sa;
check_sockaddr_len(at);
nr = &sa_at->sat_range.r_netrange;
printf("%d.%d, %d-%d, %d", ntohs(sa_at->sat_addr.s_net),
sa_at->sat_addr.s_node, ntohs(nr->nr_firstnet),
ntohs(nr->nr_lastnet), nr->nr_phase);
break;
}
#endif
case AF_INET6: {
struct sockaddr_in6 *sa_in6;
sa_in6 = (struct sockaddr_in6 *)sa;
check_sockaddr_len(in6);
inet_ntop(AF_INET6, &sa_in6->sin6_addr, addr, sizeof addr);
printf("[%s]:%u", addr, htons(sa_in6->sin6_port));
break;
}
#ifdef IPX
case AF_IPX: {
struct sockaddr_ipx *sa_ipx;
sa_ipx = (struct sockaddr_ipx *)sa;
check_sockaddr_len(ipx);
/* XXX wish we had ipx_ntop */
printf("%s", ipx_ntoa(sa_ipx->sipx_addr));
break;
}
#endif
case AF_UNIX: {
struct sockaddr_un *sa_un;
sa_un = (struct sockaddr_un *)sa;
check_sockaddr_len(un);
printf("%.*s", (int)sizeof(sa_un->sun_path), sa_un->sun_path);
break;
}
default:
printf("unknown address family");
}
printf(" }\n");
}
void
ktrstat(struct stat *statp)
{
char mode[12], timestr[PATH_MAX + 4];
struct passwd *pwd;
struct group *grp;
struct tm *tm;
/*
* note: ktrstruct() has already verified that statp points to a
* buffer exactly sizeof(struct stat) bytes long.
*/
printf("struct stat {");
strmode(statp->st_mode, mode);
printf("dev=%ju, ino=%ju, mode=%s, nlink=%ju, ",
(uintmax_t)statp->st_dev, (uintmax_t)statp->st_ino, mode,
(uintmax_t)statp->st_nlink);
if (resolv == 0 || (pwd = getpwuid(statp->st_uid)) == NULL)
printf("uid=%ju, ", (uintmax_t)statp->st_uid);
else
printf("uid=\"%s\", ", pwd->pw_name);
if (resolv == 0 || (grp = getgrgid(statp->st_gid)) == NULL)
printf("gid=%ju, ", (uintmax_t)statp->st_gid);
else
printf("gid=\"%s\", ", grp->gr_name);
printf("rdev=%ju, ", (uintmax_t)statp->st_rdev);
printf("atime=");
if (resolv == 0)
printf("%ld", statp->st_atimespec.tv_sec);
else {
tm = localtime(&statp->st_atimespec.tv_sec);
(void)strftime(timestr, sizeof(timestr), TIME_FORMAT, tm);
printf("\"%s\"", timestr);
}
if (statp->st_atimespec.tv_nsec != 0)
printf(".%09ld, ", statp->st_atimespec.tv_nsec);
else
printf(", ");
printf("stime=");
if (resolv == 0)
printf("%ld", statp->st_mtimespec.tv_sec);
else {
tm = localtime(&statp->st_mtimespec.tv_sec);
(void)strftime(timestr, sizeof(timestr), TIME_FORMAT, tm);
printf("\"%s\"", timestr);
}
if (statp->st_mtimespec.tv_nsec != 0)
printf(".%09ld, ", statp->st_mtimespec.tv_nsec);
else
printf(", ");
printf("ctime=");
if (resolv == 0)
printf("%ld", statp->st_ctimespec.tv_sec);
else {
tm = localtime(&statp->st_ctimespec.tv_sec);
(void)strftime(timestr, sizeof(timestr), TIME_FORMAT, tm);
printf("\"%s\"", timestr);
}
if (statp->st_ctimespec.tv_nsec != 0)
printf(".%09ld, ", statp->st_ctimespec.tv_nsec);
else
printf(", ");
printf("birthtime=");
if (resolv == 0)
printf("%ld", statp->st_birthtimespec.tv_sec);
else {
tm = localtime(&statp->st_birthtimespec.tv_sec);
(void)strftime(timestr, sizeof(timestr), TIME_FORMAT, tm);
printf("\"%s\"", timestr);
}
if (statp->st_birthtimespec.tv_nsec != 0)
printf(".%09ld, ", statp->st_birthtimespec.tv_nsec);
else
printf(", ");
printf("size=%jd, blksize=%ju, blocks=%jd, flags=0x%x",
(uintmax_t)statp->st_size, (uintmax_t)statp->st_blksize,
(intmax_t)statp->st_blocks, statp->st_flags);
printf(" }\n");
}
void
ktrstruct(char *buf, size_t buflen)
{
char *name, *data;
size_t namelen, datalen;
int i;
for (name = buf, namelen = 0;
namelen < buflen && name[namelen] != '\0';
++namelen)
/* nothing */;
if (namelen == buflen)
goto invalid;
if (name[namelen] != '\0')
goto invalid;
data = buf + namelen + 1;
datalen = buflen - namelen - 1;
if (datalen == 0)
goto invalid;
/* sanity check */
for (i = 0; i < namelen; ++i)
if (!isalpha((unsigned char)name[i]))
goto invalid;
if (strcmp(name, "stat") == 0) {
if (datalen != sizeof(struct stat))
goto invalid;
ktrstat((struct stat *)data);
} else if (strcmp(name, "sockaddr") == 0) {
if (datalen < sizeof(struct sockaddr) ||
datalen != ((struct sockaddr *)(data))->sa_len)
goto invalid;
ktrsockaddr((struct sockaddr *)data);
} else {
printf("unknown structure\n");
}
return;
invalid:
printf("invalid record\n");
}
void
usage(void)
{
fprintf(stderr, "usage: kdump [-dEnlHRrsT] [-f trfile] "
"[-m maxdata] [-p pid] [-t [cnistuw]]\n");
exit(1);
}