Source-Changes-HG archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
[src/trunk]: src Add support for trace type selection in kcov(4)
details: https://anonhg.NetBSD.org/src/rev/7e00e88ec68a
branches: trunk
changeset: 449555:7e00e88ec68a
user: kamil <kamil%NetBSD.org@localhost>
date: Sun Mar 10 17:51:00 2019 +0000
description:
Add support for trace type selection in kcov(4)
Allow to specify mode in KCOV_IOC_ENABLE synchronizing the functionality
with Linux, FreeBSD and OpenBSD. As a NetBSD (and OpenBSD) specific of
the ioctl(2) interface, the mode argument has to be specified as &value
rather than value.
There are 3 modes available:
1. KCOV_MODE_NONE -- no trace specified, useful for testing purposes
2. KCOV_MODE_TRACE_PC -- trace the kernel program counter
3. KCOV_MODE_TRACE_CMP -- trace comparison instructions and switch statements
Adapt the ATF tests and documentation for new API.
The KCOV_MODE_TRACE_CMP mode is implemented but still awaits for the
GCC 8.x upgrade or selection of Clang/LLVM as the kernel compiler.
Obtained from OpenBSD and adapted for NetBSD by myself.
diffstat:
share/man/man4/kcov.4 | 46 +++++++++++-
sys/kern/subr_kcov.c | 183 ++++++++++++++++++++++++++++++++++++++++++++++++-
sys/sys/kcov.h | 8 +-
tests/modules/t_kcov.c | 79 ++++++++++++++++----
4 files changed, 293 insertions(+), 23 deletions(-)
diffs (truncated from 546 to 300 lines):
diff -r b5cba66f6b87 -r 7e00e88ec68a share/man/man4/kcov.4
--- a/share/man/man4/kcov.4 Sun Mar 10 16:30:01 2019 +0000
+++ b/share/man/man4/kcov.4 Sun Mar 10 17:51:00 2019 +0000
@@ -1,4 +1,4 @@
-.\" $NetBSD: kcov.4,v 1.3 2019/03/10 12:54:39 kamil Exp $
+.\" $NetBSD: kcov.4,v 1.4 2019/03/10 17:51:00 kamil Exp $
.\"
.\" Copyright (c) 2018 Anton Lindqvist <anton%openbsd.org@localhost>
.\"
@@ -82,8 +82,46 @@
Note that kcov_int_t is volatile.
The first entry contains the number of entries in the array,
excluding the first entry.
-.It Dv KCOV_IOC_ENABLE Fa void
+.It Dv KCOV_IOC_ENABLE Fa int *mode
Enable code coverage tracing for the current thread.
+The
+.Fa mode
+must be one of the following:
+.Bl -ohang
+.It Dv KCOV_MODE_NONE
+No trace selected.
+This option is useful for testing the
+.Nm
+device.
+.It Dv KCOV_MODE_TRACE_PC
+Trace the kernel program counter.
+.It Dv KCOV_MODE_TRACE_CMP
+Trace comparison instructions and switch statements.
+For switch statements, the number of traced comparison instructions is equal to
+the number of switch cases.
+Each traced comparison instruction is represented by 4 entries in the coverage
+buffer:
+.Bl -enum
+.It
+A mask where the least significant bit is set if one of the comparison operands
+is a compile-time constant, which is always true for switch statements.
+The remaining bits represents the log2 size of the operands, ranging from 0 to
+3.
+.It
+First comparison operand.
+For switch statements, this operand corresponds to the case value.
+.It
+Second comparison operand.
+For switch statements, this operand corresponds to the value passed to switch.
+.It
+Kernel program counter where the comparison instruction took place.
+.El
+.Pp
+In this mode, the first entry in the coverage buffer reflects the number of
+traced comparison instructions.
+Thus, the effective number of entries in the coverage buffer is given by
+multiplying the first entry by 4.
+.El
.It Dv KCOV_IOC_DISABLE Fa void
Disable code coverage tracing for the current thread.
.El
@@ -119,6 +157,7 @@
kcov_int_t *cover, i, n;
uint64_t size = 1024 * 100;
int fd;
+ int mode;
fd = open("/dev/kcov", O_RDWR);
if (fd == -1)
@@ -129,7 +168,8 @@
PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
if (cover == MAP_FAILED)
err(1, "mmap");
- if (ioctl(fd, KCOV_IOC_ENABLE) == -1)
+ mode = KCOV_MODE_TRACE_PC;
+ if (ioctl(fd, KCOV_IOC_ENABLE, &mode) == -1)
err(1, "ioctl: KCOV_IOC_ENABLE");
KCOV_STORE(cover[0], 0);
read(-1, NULL, 0); /* syscall paths to be traced */
diff -r b5cba66f6b87 -r 7e00e88ec68a sys/kern/subr_kcov.c
--- a/sys/kern/subr_kcov.c Sun Mar 10 16:30:01 2019 +0000
+++ b/sys/kern/subr_kcov.c Sun Mar 10 17:51:00 2019 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: subr_kcov.c,v 1.4 2019/03/10 12:54:39 kamil Exp $ */
+/* $NetBSD: subr_kcov.c,v 1.5 2019/03/10 17:51:00 kamil Exp $ */
/*
* Copyright (c) 2019 The NetBSD Foundation, Inc.
@@ -50,6 +50,9 @@
#define KCOV_BUF_MAX_ENTRIES (256 << 10)
+#define KCOV_CMP_CONST 1
+#define KCOV_CMP_SIZE(x) ((x) << 1)
+
static dev_type_open(kcov_open);
const struct cdevsw kcov_cdevsw = {
@@ -107,6 +110,7 @@
struct uvm_object *uobj;
size_t bufnent;
size_t bufsize;
+ int mode;
bool enabled;
bool lwpfree;
} kcov_t;
@@ -231,6 +235,7 @@
kcov_fops_ioctl(file_t *fp, u_long cmd, void *addr)
{
int error = 0;
+ int mode;
kcov_t *kd;
kd = fp->f_data;
@@ -259,6 +264,20 @@
error = ENOBUFS;
break;
}
+
+ mode = *((int *)addr);
+ switch (mode) {
+ case KCOV_MODE_NONE:
+ case KCOV_MODE_TRACE_PC:
+ case KCOV_MODE_TRACE_CMP:
+ kd->mode = mode;
+ break;
+ default:
+ error = EINVAL;
+ }
+ if (error)
+ break;
+
lwp_setspecific(kcov_lwp_key, kd);
kd->enabled = true;
break;
@@ -356,6 +375,11 @@
return;
}
+ if (kd->mode != KCOV_MODE_TRACE_PC) {
+ /* PC tracing mode not enabled */
+ return;
+ }
+
idx = KCOV_LOAD(kd->buf[0]);
if (idx < kd->bufnent) {
KCOV_STORE(kd->buf[idx+1],
@@ -364,6 +388,163 @@
}
}
+static void
+trace_cmp(uint64_t type, uint64_t arg1, uint64_t arg2, intptr_t pc)
+{
+ extern int cold;
+ uint64_t idx;
+ kcov_t *kd;
+
+ if (__predict_false(cold)) {
+ /* Do not trace during boot. */
+ return;
+ }
+
+ if (in_interrupt()) {
+ /* Do not trace in interrupts. */
+ return;
+ }
+
+ kd = lwp_getspecific(kcov_lwp_key);
+ if (__predict_true(kd == NULL)) {
+ /* Not traced. */
+ return;
+ }
+
+ if (!kd->enabled) {
+ /* Tracing not enabled */
+ return;
+ }
+
+ if (kd->mode != KCOV_MODE_TRACE_CMP) {
+ /* PC tracing mode not enabled */
+ return;
+ }
+
+ idx = KCOV_LOAD(kd->buf[0]);
+ if ((idx * 4 + 4) <= kd->bufnent) {
+ KCOV_STORE(kd->buf[idx * 4 + 1], type);
+ KCOV_STORE(kd->buf[idx * 4 + 2], arg1);
+ KCOV_STORE(kd->buf[idx * 4 + 3], arg2);
+ KCOV_STORE(kd->buf[idx * 4 + 4], pc);
+ KCOV_STORE(kd->buf[0], idx + 1);
+ }
+}
+
+void __sanitizer_cov_trace_cmp1(uint8_t arg1, uint8_t arg2);
+
+void
+__sanitizer_cov_trace_cmp1(uint8_t arg1, uint8_t arg2)
+{
+
+ trace_cmp(KCOV_CMP_SIZE(0), arg1, arg2,
+ (intptr_t)__builtin_return_address(0));
+}
+
+void __sanitizer_cov_trace_cmp2(uint16_t arg1, uint16_t arg2);
+
+void
+__sanitizer_cov_trace_cmp2(uint16_t arg1, uint16_t arg2)
+{
+
+ trace_cmp(KCOV_CMP_SIZE(1), arg1, arg2,
+ (intptr_t)__builtin_return_address(0));
+}
+
+void __sanitizer_cov_trace_cmp4(uint32_t arg1, uint32_t arg2);
+
+void
+__sanitizer_cov_trace_cmp4(uint32_t arg1, uint32_t arg2)
+{
+
+ trace_cmp(KCOV_CMP_SIZE(2), arg1, arg2,
+ (intptr_t)__builtin_return_address(0));
+}
+
+void __sanitizer_cov_trace_cmp8(uint64_t arg1, uint64_t arg2);
+
+void
+__sanitizer_cov_trace_cmp8(uint64_t arg1, uint64_t arg2)
+{
+
+ trace_cmp(KCOV_CMP_SIZE(3), arg1, arg2,
+ (intptr_t)__builtin_return_address(0));
+}
+
+void __sanitizer_cov_trace_const_cmp1(uint8_t arg1, uint8_t arg2);
+
+void
+__sanitizer_cov_trace_const_cmp1(uint8_t arg1, uint8_t arg2)
+{
+
+ trace_cmp(KCOV_CMP_SIZE(0) | KCOV_CMP_CONST, arg1, arg2,
+ (intptr_t)__builtin_return_address(0));
+}
+
+void __sanitizer_cov_trace_const_cmp2(uint16_t arg1, uint16_t arg2);
+
+void
+__sanitizer_cov_trace_const_cmp2(uint16_t arg1, uint16_t arg2)
+{
+
+ trace_cmp(KCOV_CMP_SIZE(1) | KCOV_CMP_CONST, arg1, arg2,
+ (intptr_t)__builtin_return_address(0));
+}
+
+void __sanitizer_cov_trace_const_cmp4(uint32_t arg1, uint32_t arg2);
+
+void
+__sanitizer_cov_trace_const_cmp4(uint32_t arg1, uint32_t arg2)
+{
+
+ trace_cmp(KCOV_CMP_SIZE(2) | KCOV_CMP_CONST, arg1, arg2,
+ (intptr_t)__builtin_return_address(0));
+}
+
+void __sanitizer_cov_trace_const_cmp8(uint64_t arg1, uint64_t arg2);
+
+void
+__sanitizer_cov_trace_const_cmp8(uint64_t arg1, uint64_t arg2)
+{
+
+ trace_cmp(KCOV_CMP_SIZE(3) | KCOV_CMP_CONST, arg1, arg2,
+ (intptr_t)__builtin_return_address(0));
+}
+
+void __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases);
+
+void
+__sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases)
+{
+ uint64_t i, nbits, ncases, type;
+ intptr_t pc;
+
+ pc = (intptr_t)__builtin_return_address(0);
+ ncases = cases[0];
+ nbits = cases[1];
+
+ switch (nbits) {
+ case 8:
+ type = KCOV_CMP_SIZE(0);
+ break;
+ case 16:
+ type = KCOV_CMP_SIZE(1);
+ break;
+ case 32:
+ type = KCOV_CMP_SIZE(2);
+ break;
+ case 64:
+ type = KCOV_CMP_SIZE(3);
+ break;
+ default:
+ return;
+ }
+ type |= KCOV_CMP_CONST;
+
Home |
Main Index |
Thread Index |
Old Index