Add a "live" mode to ktrdump.

Support a "live" mode in ktrdump enabled via the -l flag.  In this
mode, ktrdump polls the kernel's trace buffer periodically (currently
hardcoded as a 50 millisecond interval) and dumps any newly added
entries.  Fancier logic for the timeout (e.g. a command line option or
some kind of backoff based on the time since the last entry) can be
added later as the need arises.

While here, fix some bugs from when this was Capsicum-ized:
- Use caph_limit_stream() for the output stream so that isatty() works
  and the output can be line-buffered (especially useful for live
  mode).
- Use caph_limit_stderr() to permit error messages to be displayed if
  an error occurs after cap_enter().

Reviewed by:	kib, 0mp (manpage)
MFC after:	2 months
Differential Revision:	https://reviews.freebsd.org/D17315
This commit is contained in:
John Baldwin 2018-10-22 21:25:28 +00:00
parent 74e10fb613
commit 4403657034
2 changed files with 62 additions and 14 deletions

View File

@ -25,7 +25,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd February 6, 2015
.Dd October 22, 2018
.Dt KTRDUMP 8
.Os
.Sh NAME
@ -33,7 +33,7 @@
.Nd print kernel ktr trace buffer
.Sh SYNOPSIS
.Nm
.Op Fl cfqrtH
.Op Fl cflqrtH
.Op Fl i Ar ktrfile
.Op Fl M Ar core
.Op Fl N Ar system
@ -47,6 +47,9 @@ The following options are available:
.Bl -tag -width ".Fl i Ar ktrfile"
.It Fl c
Print the CPU number that each entry was logged from.
.It Fl l
Poll the kernel ktr trace buffer periodically dumping any new events after
each poll.
.It Fl f
Print the file and line number that each entry was logged from.
.It Fl q

View File

@ -51,7 +51,7 @@ __FBSDID("$FreeBSD$");
#define SBUFLEN 128
#define USAGE \
"usage: ktrdump [-cfqrtH] [-i ktrfile] [-M core] [-N system] [-o outfile]\n"
"usage: ktrdump [-cflqrtH] [-i ktrfile] [-M core] [-N system] [-o outfile]\n"
static void usage(void);
@ -65,6 +65,7 @@ static struct nlist nl[] = {
static int cflag;
static int fflag;
static int lflag;
static int Mflag;
static int Nflag;
static int qflag;
@ -111,7 +112,7 @@ main(int ac, char **av)
* Parse commandline arguments.
*/
out = stdout;
while ((c = getopt(ac, av, "cfqrtHe:i:m:M:N:o:")) != -1)
while ((c = getopt(ac, av, "cflqrtHe:i:m:M:N:o:")) != -1)
switch (c) {
case 'c':
cflag = 1;
@ -136,6 +137,9 @@ main(int ac, char **av)
err(1, "unable to limit rights for %s",
optarg);
break;
case 'l':
lflag = 1;
break;
case 'M':
case 'm':
if (strlcpy(corefile, optarg, sizeof(corefile))
@ -169,9 +173,10 @@ main(int ac, char **av)
if (ac != 0)
usage();
cap_rights_init(&rights, CAP_FSTAT, CAP_WRITE);
if (cap_rights_limit(fileno(out), &rights) < 0 && errno != ENOSYS)
if (caph_limit_stream(fileno(out), CAPH_WRITE) < 0)
err(1, "unable to limit rights for %s", outfile);
if (caph_limit_stderr() < 0)
err(1, "unable to limit rights for stderr");
/*
* Open our execfile and corefile, resolve needed symbols and read in
@ -258,15 +263,29 @@ main(int ac, char **av)
fprintf(out, "\n");
}
tlast = -1;
/*
* Now tear through the trace buffer.
*
* In "live" mode, find the oldest entry (first non-NULL entry
* after index2) and walk forward. Otherwise, start with the
* most recent entry and walk backwards.
*/
if (!iflag) {
i = index - 1;
if (i < 0)
i = entries - 1;
if (lflag) {
i = index2 + 1 % entries;
while (buf[i].ktr_desc == NULL && i != index) {
i++;
if (i == entries)
i = 0;
}
} else {
i = index - 1;
if (i < 0)
i = entries - 1;
}
}
tlast = -1;
dump_entries:
for (;;) {
if (buf[i].ktr_desc == NULL)
break;
@ -338,16 +357,42 @@ next: if ((c = *p++) == '\0')
* 'index2' were in flux while the KTR buffer was
* being copied to userspace we don't dump them.
*/
if (i == index2)
break;
if (--i < 0)
i = entries - 1;
if (lflag) {
if (++i == entries)
i = 0;
if (i == index)
break;
} else {
if (i == index2)
break;
if (--i < 0)
i = entries - 1;
}
} else {
if (++i == entries)
break;
}
}
/*
* In "live" mode, poll 'ktr_idx' periodically and dump any
* new entries since our last pass through the ring.
*/
if (lflag && !iflag) {
while (index == index2) {
usleep(50 * 1000);
if (kvm_read(kd, nl[2].n_value, &index2,
sizeof(index2)) == -1)
errx(1, "%s", kvm_geterr(kd));
}
i = index;
index = index2;
if (kvm_read(kd, bufptr, buf, sizeof(*buf) * entries) == -1 ||
kvm_read(kd, nl[2].n_value, &index2, sizeof(index2)) == -1)
errx(1, "%s", kvm_geterr(kd));
goto dump_entries;
}
return (0);
}