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