NetBSD/usr.bin/ktruss/dump.c
enami 1f8c4c1f20 Rototill for less confusing output, especially when multiple processes
are traced and blocking operation is involved (previously, some system
call isn't displayed or displayed as if it is issued by other process).
2004-02-26 22:00:57 +00:00

753 lines
15 KiB
C

/* $NetBSD: dump.c,v 1.18 2004/02/26 22:00:57 enami 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/cdefs.h>
#ifndef lint
__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.4 (Berkeley) 4/28/95";
#endif
__RCSID("$NetBSD: dump.c,v 1.18 2004/02/26 22:00:57 enami Exp $");
#endif /* not lint */
#include <sys/param.h>
#define _KERNEL
#include <sys/errno.h>
#undef _KERNEL
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/uio.h>
#include <sys/ktrace.h>
#include <sys/ptrace.h>
#include <sys/queue.h>
#include <err.h>
#include <signal.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <vis.h>
#include "ktrace.h"
#include "misc.h"
#include "setemul.h"
int timestamp, decimal, fancy = 1, tail, maxdata;
int width; /* Keep track of current columns. */
#include <sys/syscall.h>
static const char *const 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",
};
struct ktr_entry {
TAILQ_ENTRY(ktr_entry) kte_list;
struct ktr_header kte_kth;
};
TAILQ_HEAD(kteq, ktr_entry) ktependq = TAILQ_HEAD_INITIALIZER(ktependq);
void argprint(const char *, register_t **, int *);
void dumpheader(struct ktr_header *);
int dumprecord(int, FILE *);
void flushpendq(struct ktr_entry *);
int fread_tail(void *, int, int, FILE *);
void genioprint(struct ktr_header *);
struct ktr_entry *
getpendq(struct ktr_header *, int, struct kteq *);
struct ktr_entry *
getrecord(FILE *);
void indent(int);
void ioctldecode(u_long);
void ktrcsw(struct ktr_entry *);
void ktremul(struct ktr_entry *);
void ktrgenio(struct ktr_entry *);
void ktrnamei(struct ktr_entry *);
void ktrpsig(struct ktr_entry *);
void ktrsyscall(struct ktr_entry *);
void ktrsysret(struct ktr_entry *);
void nameiargprint(const char *, struct ktr_header *, register_t **, int *);
void nameiprint(struct ktr_header *);
void newline(void);
void putpendq(struct ktr_entry *);
void syscallnameprint(int);
void syscallprint(struct ktr_header *);
void sysretprint(struct ktr_header *);
int wprintf(const char *, ...);
void *xrealloc(void *, size_t *, size_t);
int
wprintf(const char *fmt, ...)
{
va_list ap;
int w;
va_start(ap, fmt);
w = vprintf(fmt, ap);
if (w == -1)
warn("vprintf");
else
width += w;
va_end(ap);
return (w);
}
void
newline(void)
{
if (width > 0) {
printf("\n");
width = 0;
}
}
void
indent(int col)
{
while (width < col)
if (wprintf(" ") < 0)
break;
}
void *
xrealloc(void *p, size_t *siz, size_t req)
{
if (*siz < req) {
if (*siz == 0)
*siz = 1;
while (*siz < req)
*siz <<= 1;
p = realloc(p, *siz);
if (p == NULL)
err(EXIT_FAILURE, "realloc: %lu bytes",
(u_long)*siz);
}
return (p);
}
struct ktr_entry *
getrecord(FILE *fp)
{
struct ktr_entry *kte;
struct ktr_header *kth;
char *cp;
size_t siz, len;
siz = 0;
kte = xrealloc(NULL, &siz, sizeof(struct ktr_entry));
kth = &kte->kte_kth;
if (fread_tail(kth, sizeof(struct ktr_header), 1, fp) == 0) {
free(kte);
return (NULL);
}
len = kth->ktr_len;
if (len < 0)
errx(EXIT_FAILURE, "bogus length 0x%lx", (long)len);
if (len > 0) {
/* + 1 to ensure room for NUL terminate */
kte = xrealloc(kte, &siz, sizeof(struct ktr_entry) + len + 1);
if (fread_tail(cp = (char *)(&kte->kte_kth + 1),
len, 1, fp) == 0)
errx(EXIT_FAILURE, "data too short");
cp[len] = 0;
}
return (kte);
}
/* XXX: lwp. */
#define KTE_TYPE(kte) ((kte)->kte_kth.ktr_type)
#define KTE_PID(kte) ((kte)->kte_kth.ktr_pid)
#define KTE_MATCH(kte, type, pid) \
(KTE_TYPE(kte) == (type) && KTE_PID(kte) == (pid))
void
putpendq(struct ktr_entry *kte)
{
TAILQ_INSERT_TAIL(&ktependq, kte, kte_list);
}
void
flushpendq(struct ktr_entry *us)
{
struct ktr_entry *kte, *kte_next;
int pid = KTE_PID(us);
for (kte = TAILQ_FIRST(&ktependq); kte != NULL; kte = kte_next) {
kte_next = TAILQ_NEXT(kte, kte_list);
if (KTE_PID(kte) == pid) {
TAILQ_REMOVE(&ktependq, kte, kte_list);
free(kte);
}
}
}
struct ktr_entry *
getpendq(struct ktr_header *us, int type, struct kteq *kteq)
{
struct ktr_entry *kte, *kte_next;
int pid = us->ktr_pid;
if (kteq != NULL)
TAILQ_INIT(kteq);
for (kte = TAILQ_FIRST(&ktependq); kte != NULL; kte = kte_next) {
kte_next = TAILQ_NEXT(kte, kte_list);
if (KTE_MATCH(kte, type, pid)) {
TAILQ_REMOVE(&ktependq, kte, kte_list);
if (kteq != NULL)
TAILQ_INSERT_TAIL(kteq, kte, kte_list);
else
break;
}
}
return (kteq ? TAILQ_FIRST(kteq) : kte);
}
int
dumprecord(int trpoints, FILE *fp)
{
struct ktr_entry *kte;
struct ktr_header *kth;
kte = getrecord(fp);
if (kte == NULL)
return (0);
kth = &kte->kte_kth;
if ((trpoints & (1 << kth->ktr_type)) == 0) {
free(kte);
goto out;
}
/* Update context to match currently processed record. */
ectx_sanify(kth->ktr_pid);
switch (kth->ktr_type) {
case KTR_SYSCALL:
ktrsyscall(kte);
break;
case KTR_SYSRET:
ktrsysret(kte);
break;
case KTR_NAMEI:
putpendq(kte);
break;
case KTR_GENIO:
putpendq(kte);
break;
case KTR_PSIG:
ktrpsig(kte);
break;
case KTR_CSW:
ktrcsw(kte);
break;
case KTR_EMUL:
ktremul(kte);
break;
default:
/*
* XXX: Other types added recently.
*/
free(kte);
break;
}
newline();
out:
return (1);
}
void
dumpfile(const char *file, int fd, int trpoints)
{
FILE *fp;
if (file == NULL || *file == 0) {
if ((fp = fdopen(fd, "r")) == NULL)
err(EXIT_FAILURE, "fdopen(%d)", fd);
} else if (strcmp(file, "-") == 0)
fp = stdin;
else if ((fp = fopen(file, "r")) == NULL)
err(EXIT_FAILURE, "fopen: %s", file);
for (width = 0; dumprecord(trpoints, fp) != 0;)
if (tail)
(void)fflush(stdout);
newline();
/*
* XXX: Dump pending KTR_SYSCALL if any?
*/
}
int
fread_tail(void *buf, int size, int num, FILE *fp)
{
int i;
while ((i = fread(buf, size, num, fp)) == 0 && tail) {
(void)sleep(1);
clearerr(fp);
}
return (i);
}
void
dumpheader(struct ktr_header *kth)
{
static struct timeval prevtime;
struct timeval temp;
wprintf("%6d %-8.*s ", kth->ktr_pid, MAXCOMLEN, kth->ktr_comm);
if (timestamp) {
if (timestamp == 2) {
timersub(&kth->ktr_time, &prevtime, &temp);
prevtime = kth->ktr_time;
} else
temp = kth->ktr_time;
wprintf("%ld.%06ld ",
(long int)temp.tv_sec, (long int)temp.tv_usec);
}
}
void
ioctldecode(u_long cmd)
{
char dirbuf[4], *dir = dirbuf;
if (cmd & IOC_OUT)
*dir++ = 'W';
if (cmd & IOC_IN)
*dir++ = 'R';
*dir = '\0';
wprintf(decimal ? ", _IO%s('%c',%ld" : ", _IO%s('%c',%#lx",
dirbuf, (int) ((cmd >> 8) & 0xff), cmd & 0xff);
if ((cmd & IOC_VOID) == 0)
wprintf(decimal ? ",%ld)" : ",%#lx)",
(cmd >> 16) & 0xff);
else
wprintf(")");
}
void
nameiargprint(const char *prefix, struct ktr_header *kth,
register_t **ap, int *argsize)
{
struct ktr_entry *kte;
if (*argsize == 0)
errx(EXIT_FAILURE, "argument expected");
/*
* XXX: binary emulation mode.
*/
kte = getpendq(kth, KTR_NAMEI, NULL);
if (kte == NULL)
argprint(prefix, ap, argsize);
else {
wprintf("%s", prefix);
nameiprint(&kte->kte_kth);
free(kte);
(*ap)++;
*argsize -= sizeof(register_t);
}
}
void
syscallnameprint(int code)
{
if (code >= cur_emul->nsysnames || code < 0)
wprintf("[%d]", code);
else
wprintf("%s", cur_emul->sysnames[code]);
}
void
argprint(const char *prefix, register_t **ap, int *argsize)
{
if (decimal)
wprintf("%s%ld", prefix, (long)**ap);
else
wprintf("%s%#lx", prefix, (long)**ap);
(*ap)++;
*argsize -= sizeof(register_t);
}
void
syscallprint(struct ktr_header *kth)
{
struct ktr_syscall *ktr = (struct ktr_syscall *)(kth + 1);
register_t *ap;
char *s;
int argsize;
syscallnameprint(ktr->ktr_code);
/*
* Arguments processing.
*/
argsize = ktr->ktr_argsize;
if (argsize == 0) {
wprintf("(");
goto noargument;
}
ap = (register_t *)(ktr + 1);
if (!fancy)
goto print_first;
switch (ktr->ktr_code) {
/*
* All these have a path as the first param.
* The order is same as syscalls.master.
*/
case SYS_open:
case SYS_link:
case SYS_unlink:
case SYS_chdir:
case SYS_mknod:
case SYS_chmod:
case SYS_chown:
case SYS_unmount:
case SYS_access:
case SYS_chflags:
case SYS_acct:
case SYS_revoke:
case SYS_symlink:
case SYS_readlink:
case SYS_execve:
case SYS_chroot:
case SYS_rename:
case SYS_mkfifo:
case SYS_mkdir:
case SYS_rmdir:
case SYS_utimes:
case SYS_quotactl:
case SYS_statfs:
case SYS_getfh:
case SYS_pathconf:
case SYS_truncate:
case SYS_undelete:
case SYS___posix_rename:
case SYS_lchmod:
case SYS_lchown:
case SYS_lutimes:
case SYS___stat13:
case SYS___lstat13:
case SYS___posix_chown:
case SYS___posix_lchown:
case SYS_lchflags:
nameiargprint("(", kth, &ap, &argsize);
/*
* 2nd argument is also pathname.
*/
switch (ktr->ktr_code) {
case SYS_link:
case SYS_rename:
case SYS___posix_rename:
nameiargprint(", ", kth, &ap, &argsize);
break;
}
break;
case SYS_compat_16___sigaction14 :
wprintf("(%s", signals[(int)*ap].name);
ap++;
argsize -= sizeof(register_t);
break;
case SYS_ioctl :
argprint("(", &ap, &argsize);
if ((s = ioctlname(*ap)) != NULL)
wprintf(", %s", s);
else
ioctldecode(*ap);
ap++;
argsize -= sizeof(register_t);
break;
case SYS_ptrace :
if (*ap >= 0 &&
*ap <= sizeof(ptrace_ops) / sizeof(ptrace_ops[0]))
wprintf("(%s", ptrace_ops[*ap]);
else
wprintf("(%ld", (long)*ap);
ap++;
argsize -= sizeof(register_t);
break;
default:
print_first:
argprint("(", &ap, &argsize);
break;
}
/* Print rest of argument. */
while (argsize > 0)
argprint(", ", &ap, &argsize);
noargument:
wprintf(")");
}
void
ktrsyscall(struct ktr_entry *kte)
{
struct ktr_header *kth = &kte->kte_kth;
struct ktr_syscall *ktr = (struct ktr_syscall *)(kth + 1);
switch (ktr->ktr_code) {
case SYS_exit:
dumpheader(kth);
syscallprint(kth);
break;
default:
putpendq(kte);
return;
}
free(kte);
}
void
sysretprint(struct ktr_header *kth)
{
struct ktr_sysret *ktr = (struct ktr_sysret *)(kth + 1);
register_t ret = ktr->ktr_retval;
int error = ktr->ktr_error;
indent(50);
if (error == EJUSTRETURN)
wprintf(" JUSTRETURN");
else if (error == ERESTART)
wprintf(" RESTART");
else if (error) {
wprintf(" Err#%d", error);
if (error < MAXERRNOS && error >= -2)
wprintf(" %s", errnos[error].name);
} else
switch (ktr->ktr_code) {
case SYS_mmap:
wprintf(" = %p", (long)ret);
break;
default:
wprintf(" = %ld", (long)ret);
if (kth->ktr_len > offsetof(struct ktr_sysret,
ktr_retval_1) && ktr->ktr_retval_1 != 0)
wprintf(", %ld", (long)ktr->ktr_retval_1);
break;
}
}
void
ktrsysret(struct ktr_entry *kte)
{
struct ktr_header *kth = &kte->kte_kth;
struct ktr_sysret *ktr = (struct ktr_sysret *)(kth + 1);
struct ktr_entry *genio;
struct ktr_entry *syscall;
dumpheader(kth);
/* Print syscall name and arguments. */
syscall = getpendq(kth, KTR_SYSCALL, NULL);
if (syscall == NULL)
/*
* Possibilly a child of fork/vfork, or tracing of
* process started during system call.
*/
syscallnameprint(ktr->ktr_code);
else {
syscallprint(&syscall->kte_kth);
free(syscall);
}
/* Print return value and an error if any. */
sysretprint(kth);
genio = getpendq(kth, KTR_GENIO, NULL);
if (genio != NULL) {
genioprint(&genio->kte_kth);
free(genio);
}
flushpendq(kte);
free(kte);
}
void
nameiprint(struct ktr_header *kth)
{
wprintf("\"%.*s\"", kth->ktr_len, (char *)(kth + 1));
}
#ifdef notused
void
ktrnamei(struct ktr_entry *kte)
{
struct ktr_header *kth = &kte->kte_kth;
dumpheader(kth);
wprintf("namei(");
nameiprint(kth);
wprintf(")");
free(kte);
}
#endif
void
ktremul(struct ktr_entry *kte)
{
struct ktr_header *kth = &kte->kte_kth;
char *emul = (char *)(kth + 1);
dumpheader(kth);
wprintf("emul(%s)", emul);
setemul(emul, kth->ktr_pid, 1);
free(kte);
}
void
genioprint(struct ktr_header *kth)
{
struct ktr_genio *ktr = (struct ktr_genio *)(kth + 1);
static int screenwidth = 0;
int datalen = kth->ktr_len - sizeof(struct ktr_genio);
/*
* Need to be unsigned type so that positive value is passed
* to vis(), which will call isgraph().
*/
unsigned char *dp = (unsigned char *)(ktr + 1);
int w;
char visbuf[5];
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;
}
if (maxdata && datalen > maxdata)
datalen = maxdata;
newline();
wprintf(" \"");
for (; datalen > 0; datalen--, dp++) {
(void) vis(visbuf, *dp, VIS_NL|VIS_TAB|VIS_CSTYLE,
/* We put NUL at the end of buffer when reading */
*(dp + 1));
visbuf[4] = '\0';
w = strlen(visbuf);
if (width + w + 2 >= screenwidth)
break;
wprintf("%s", visbuf);
if (width + 2 >= screenwidth)
break;
}
wprintf("\"");
}
#ifdef notused
void
ktrgenio(struct ktr_entry *kte)
{
struct ktr_header *kth = &kte->kte_kth;
struct ktr_genio *ktr = (struct ktr_genio *)(kth + 1);
dumpheader(kth);
wprintf("genio fd %d %s",
ktr->ktr_fd, ktr->ktr_rw ? "write" : "read");
genioprint(kth);
free(kte);
}
#endif
void
ktrpsig(struct ktr_entry *kte)
{
struct ktr_header *kth = &kte->kte_kth;
struct ktr_psig *psig = (struct ktr_psig *)(kth + 1);
dumpheader(kth);
wprintf("SIG%s ", sys_signame[psig->signo]);
if (psig->action == SIG_DFL)
wprintf("SIG_DFL");
else {
wprintf("caught handler=0x%lx mask=0x%lx code=0x%x",
(u_long)psig->action, (unsigned long)psig->mask.__bits[0],
psig->code);
}
free(kte);
}
void
ktrcsw(struct ktr_entry *kte)
{
struct ktr_header *kth = &kte->kte_kth;
struct ktr_csw *cs = (struct ktr_csw *)(kth + 1);
dumpheader(kth);
wprintf("%s %s", cs->out ? "stop" : "resume",
cs->user ? "user" : "kernel");
free(kte);
}