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
This commit is contained in:
Dag-Erling Smørgrav 2008-02-23 01:01:49 +00:00
parent e75f77ece7
commit 60e15db992
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=176471
11 changed files with 358 additions and 14 deletions

View File

@ -39,6 +39,7 @@ __FBSDID("$FreeBSD$");
#include "opt_compat.h"
#include "opt_ddb.h"
#include "opt_ktrace.h"
#include <sys/param.h>
#include <sys/systm.h>
@ -72,6 +73,9 @@ __FBSDID("$FreeBSD$");
#include <sys/unistd.h>
#include <sys/user.h>
#include <sys/vnode.h>
#ifdef KTRACE
#include <sys/ktrace.h>
#endif
#include <security/audit/audit.h>
@ -1126,6 +1130,10 @@ kern_fstat(struct thread *td, int fd, struct stat *sbp)
error = fo_stat(fp, sbp, td->td_ucred, td);
fdrop(fp, td);
#ifdef KTRACE
if (error == 0 && KTRPOINT(td, KTR_STRUCT))
ktrstat(sbp);
#endif
return (error);
}

View File

@ -51,6 +51,8 @@ __FBSDID("$FreeBSD$");
#include <sys/proc.h>
#include <sys/unistd.h>
#include <sys/vnode.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/ktrace.h>
#include <sys/sx.h>
#include <sys/sysctl.h>
@ -107,7 +109,8 @@ static int data_lengths[] = {
sizeof(struct ktr_genio), /* KTR_GENIO */
sizeof(struct ktr_psig), /* KTR_PSIG */
sizeof(struct ktr_csw), /* KTR_CSW */
0 /* KTR_USER */
0, /* KTR_USER */
0, /* KTR_STRUCT */
};
static STAILQ_HEAD(, ktr_request) ktr_free;
@ -552,6 +555,33 @@ ktrcsw(out, user)
kc->user = user;
ktr_enqueuerequest(curthread, req);
}
void
ktrstruct(name, namelen, data, datalen)
const char *name;
size_t namelen;
void *data;
size_t datalen;
{
struct ktr_request *req;
char *buf = NULL;
size_t buflen;
if (!data)
datalen = 0;
buflen = namelen + 1 + datalen;
buf = malloc(buflen, M_KTRACE, M_WAITOK);
bcopy(name, buf, namelen);
buf[namelen] = '\0';
bcopy(data, buf + namelen + 1, datalen);
if ((req = ktr_getrequest(KTR_STRUCT)) == NULL) {
free(buf, M_KTRACE);
return;
}
req->ktr_buffer = buf;
req->ktr_header.ktr_len = buflen;
ktr_submitrequest(curthread, req);
}
#endif /* KTRACE */
/* Interface and common routines */

View File

@ -222,6 +222,10 @@ kern_bind(td, fd, sa)
if (error)
return (error);
so = fp->f_data;
#ifdef KTRACE
if (KTRPOINT(td, KTR_STRUCT))
ktrsockaddr(sa);
#endif
#ifdef MAC
SOCK_LOCK(so);
error = mac_socket_check_bind(td->td_ucred, so, sa);
@ -444,6 +448,10 @@ kern_accept(struct thread *td, int s, struct sockaddr **name,
/* check sa_len before it is destroyed */
if (*namelen > sa->sa_len)
*namelen = sa->sa_len;
#ifdef KTRACE
if (KTRPOINT(td, KTR_STRUCT))
ktrsockaddr(sa);
#endif
*name = sa;
sa = NULL;
}
@ -538,6 +546,10 @@ kern_connect(td, fd, sa)
error = EALREADY;
goto done1;
}
#ifdef KTRACE
if (KTRPOINT(td, KTR_STRUCT))
ktrsockaddr(sa);
#endif
#ifdef MAC
SOCK_LOCK(so);
error = mac_socket_check_connect(td->td_ucred, so, sa);
@ -1052,6 +1064,10 @@ kern_recvit(td, s, mp, fromseg, controlp)
}
out:
fdrop(fp, td);
#ifdef KTRACE
if (fromsa && KTRPOINT(td, KTR_STRUCT))
ktrsockaddr(fromsa);
#endif
if (fromsa)
FREE(fromsa, M_SONAME);
@ -1456,6 +1472,10 @@ kern_getsockname(struct thread *td, int fd, struct sockaddr **sa,
else
len = MIN(*alen, (*sa)->sa_len);
*alen = len;
#ifdef KTRACE
if (KTRPOINT(td, KTR_STRUCT))
ktrsockaddr(*sa);
#endif
bad:
fdrop(fp, td);
if (error && *sa) {
@ -1553,6 +1573,10 @@ kern_getpeername(struct thread *td, int fd, struct sockaddr **sa,
else
len = MIN(*alen, (*sa)->sa_len);
*alen = len;
#ifdef KTRACE
if (KTRPOINT(td, KTR_STRUCT))
ktrsockaddr(*sa);
#endif
bad:
if (error && *sa) {
free(*sa, M_SONAME);
@ -2373,6 +2397,10 @@ sctp_generic_sendmsg (td, uap)
error = getsock(td->td_proc->p_fd, uap->sd, &fp, NULL);
if (error)
goto sctp_bad;
#ifdef KTRACE
if (KTRPOINT(td, KTR_STRUCT))
ktrsockaddr(to);
#endif
iov[0].iov_base = uap->msg;
iov[0].iov_len = uap->mlen;
@ -2476,6 +2504,10 @@ sctp_generic_sendmsg_iov(td, uap)
error = copyiniov(uap->iov, uap->iovlen, &iov, EMSGSIZE);
if (error)
goto sctp_bad1;
#ifdef KTRACE
if (KTRPOINT(td, KTR_STRUCT))
ktrsockaddr(to);
#endif
so = (struct socket *)fp->f_data;
#ifdef MAC
@ -2659,6 +2691,10 @@ sctp_generic_recvmsg(td, uap)
goto out;
}
}
#ifdef KTRACE
if (KTRPOINT(td, KTR_STRUCT))
ktrsockaddr(fromsa);
#endif
if (uap->msg_flags) {
error = copyout(&msg_flags, uap->msg_flags, sizeof (int));
if (error) {

View File

@ -38,6 +38,7 @@
__FBSDID("$FreeBSD$");
#include "opt_compat.h"
#include "opt_ktrace.h"
#include "opt_mac.h"
#include <sys/param.h>
@ -67,6 +68,9 @@ __FBSDID("$FreeBSD$");
#include <sys/jail.h>
#include <sys/syscallsubr.h>
#include <sys/sysctl.h>
#ifdef KTRACE
#include <sys/ktrace.h>
#endif
#include <machine/stdarg.h>
@ -2120,6 +2124,10 @@ kern_stat(struct thread *td, char *path, enum uio_seg pathseg, struct stat *sbp)
if (error)
return (error);
*sbp = sb;
#ifdef KTRACE
if (KTRPOINT(td, KTR_STRUCT))
ktrstat(&sb);
#endif
return (0);
}
@ -2171,6 +2179,10 @@ kern_lstat(struct thread *td, char *path, enum uio_seg pathseg, struct stat *sbp
if (error)
return (error);
*sbp = sb;
#ifdef KTRACE
if (KTRPOINT(td, KTR_STRUCT))
ktrstat(&sb);
#endif
return (0);
}

View File

@ -150,6 +150,13 @@ struct ktr_csw {
#define KTR_USER_MAXLEN 2048 /* maximum length of passed data */
#define KTR_USER 7
/*
* KTR_STRUCT - misc. structs
*/
#define KTR_STRUCT 8
struct sockaddr;
struct stat;
/*
* KTR_DROP - If this bit is set in ktr_type, then at least one event
* between the previous record and this record was dropped.
@ -167,6 +174,7 @@ struct ktr_csw {
#define KTRFAC_PSIG (1<<KTR_PSIG)
#define KTRFAC_CSW (1<<KTR_CSW)
#define KTRFAC_USER (1<<KTR_USER)
#define KTRFAC_STRUCT (1<<KTR_STRUCT)
/*
* trace flags (also in p_traceflags)
*/
@ -185,6 +193,11 @@ void ktrsyscall(int, int narg, register_t args[]);
void ktrsysret(int, int, register_t);
void ktrprocexit(struct thread *);
void ktruserret(struct thread *);
void ktrstruct(const char *, size_t, void *, size_t);
#define ktrsockaddr(s) \
ktrstruct("sockaddr", 8, (s), ((struct sockaddr *)(s))->sa_len)
#define ktrstat(s) \
ktrstruct("stat", 4, (s), sizeof(struct stat))
#else

View File

@ -32,7 +32,7 @@
.\" @(#)kdump.1 8.1 (Berkeley) 6/6/93
.\" $FreeBSD$
.\"
.Dd November 12, 2005
.Dd February 23, 2008
.Dt KDUMP 1
.Os
.Sh NAME
@ -44,7 +44,7 @@
.Op Fl f Ar trfile
.Op Fl m Ar maxdata
.Op Fl p Ar pid
.Op Fl t Op cnisuw
.Op Fl t Op cnistuw
.Sh DESCRIPTION
The
.Nm
@ -96,11 +96,14 @@ This may be useful when there are multiple processes recorded in the
same trace file.
.It Fl R
Display relative timestamps (time since previous entry).
.It Fl r
When decoding STRU records, display structure members such as UIDs,
GIDs, dates etc. symbolically instead of numerically.
.It Fl s
Suppress display of I/O data.
.It Fl T
Display absolute timestamps for each entry (seconds since epoch).
.It Fl t Ar cnisuw
.It Fl t Ar cnistuw
See the
.Fl t
option of
@ -168,6 +171,7 @@ The possible operations are:
.It Li SIG Ta signal Ta signal name, handler, mask, code
.It Li CSW Ta context switch Ta stop/resume user/kernel
.It Li USER Ta data from user process Ta the data
.It Li STRU Ta various syscalls Ta structure
.El
.Sh SEE ALSO
.Xr ktrace 1

View File

@ -58,12 +58,26 @@ extern int errno;
#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"
@ -80,13 +94,18 @@ 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;
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
@ -100,7 +119,7 @@ main(int argc, char *argv[])
(void) setlocale(LC_CTYPE, "");
while ((ch = getopt(argc,argv,"f:dElm:np:HRsTt:")) != -1)
while ((ch = getopt(argc,argv,"f:dElm:np:HRrsTt:")) != -1)
switch((char)ch) {
case 'f':
tracefile = optarg;
@ -120,6 +139,9 @@ main(int argc, char *argv[])
case 'p':
pid = atoi(optarg);
break;
case 'r':
resolv = 1;
break;
case 's':
suppressdata = 1;
break;
@ -209,6 +231,9 @@ main(int argc, char *argv[])
case KTR_USER:
ktruser(ktrlen, m);
break;
case KTR_STRUCT:
ktrstruct(m, ktrlen);
break;
default:
printf("\n");
break;
@ -260,6 +285,9 @@ dumpheader(struct ktr_header *kth)
case KTR_USER:
type = "USER";
break;
case KTR_STRUCT:
type = "STRU";
break;
default:
(void)sprintf(unknown, "UNKNOWN(%d)", kth->ktr_type);
type = unknown;
@ -1128,10 +1156,217 @@ ktruser(int len, unsigned char *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)
{
(void)fprintf(stderr,
"usage: kdump [-dEnlHRsT] [-f trfile] [-m maxdata] [-p pid] [-t [cnisuw]]\n");
fprintf(stderr, "usage: kdump [-dEnlHRrsT] [-f trfile] "
"[-m maxdata] [-p pid] [-t [cnistuw]]\n");
exit(1);
}

View File

@ -339,6 +339,7 @@ auto_switch_type "lio_listioname" "LIO_(NO)?WAIT[[:space:]]+[0-9]+" "aio.h"
auto_switch_type "minheritname" "INHERIT_[A-Z]+[[:space:]]+[0-9]+" "sys/mman.h"
auto_switch_type "quotactlname" "Q_[A-Z]+[[:space:]]+0x[0-9]+" "ufs/ufs/quota.h"
auto_if_type "sockdomainname" "PF_[[:alnum:]]+[[:space:]]+" "sys/socket.h"
auto_if_type "sockfamilyname" "AF_[[:alnum:]]+[[:space:]]+" "sys/socket.h"
auto_if_type "sockipprotoname" "IPPROTO_[[:alnum:]]+[[:space:]]+" "netinet/in.h"
auto_switch_type "sockoptname" "SO_[A-Z]+[[:space:]]+0x[0-9]+" "sys/socket.h"
auto_switch_type "socktypename" "SOCK_[A-Z]+[[:space:]]+[1-9]+[0-9]*" "sys/socket.h"

View File

@ -32,7 +32,7 @@
.\" @(#)ktrace.1 8.1 (Berkeley) 6/6/93
.\" $FreeBSD$
.\"
.Dd November 6, 2006
.Dd February 23, 2008
.Dt KTRACE 1
.Os
.Sh NAME
@ -43,11 +43,11 @@
.Op Fl aCcdi
.Op Fl f Ar trfile
.Op Fl g Ar pgrp | Fl p Ar pid
.Op Fl t Ar trstr
.Op Fl t Ar cnistuw
.Nm
.Op Fl adi
.Op Fl f Ar trfile
.Op Fl t Ar trstr
.Op Fl t Ar cnistuw
.Ar command
.Sh DESCRIPTION
The
@ -105,7 +105,7 @@ processes.
Enable (disable) tracing on the indicated process id (only one
.Fl p
flag is permitted).
.It Fl t Ar trstr
.It Fl t Ar cnistuw
The string argument represents the kernel trace points, one per letter.
The following table equates the letters with the tracepoints:
.Pp
@ -119,13 +119,15 @@ trace
.Tn I/O
.It Cm s
trace signal processing
.It Cm t
trace various structures
.It Cm u
userland traces
.It Cm w
context switches
.It Cm +
trace the default set of trace points -
.Cm c , n , i , s , u
.Cm c , n , i , s , t , u
.El
.It Ar command
Execute

View File

@ -35,7 +35,7 @@
*/
#define DEF_POINTS (KTRFAC_SYSCALL | KTRFAC_SYSRET | KTRFAC_NAMEI | \
KTRFAC_GENIO | KTRFAC_PSIG | KTRFAC_USER)
KTRFAC_GENIO | KTRFAC_PSIG | KTRFAC_USER | KTRFAC_STRUCT)
#define ALL_POINTS (DEF_POINTS | KTRFAC_CSW)

View File

@ -74,6 +74,9 @@ getpoints(char *s)
case 's':
facs |= KTRFAC_PSIG;
break;
case 't':
facs |= KTRFAC_STRUCT;
break;
case 'u':
facs |= KTRFAC_USER;
break;