KCOV(4)

πŸ“¦
version 15.1 Β· 3 versions
, in freebsd 14.5, freebsd 15.1, nixpkgs unstable

πŸ’… KCOV(4) β€” the kernel coverage bestie 🌸

okay so real talk, this is the kcov page from the Kernel Interfaces Manual, and it is genuinely one of the coolest little interfaces in the kernel, no cap. sit down, grab a strawberry drink πŸ“, and let's get into it.


✨ NAME ✨

kcov β€” the interface for collecting kernel code coverage information πŸŽ€


πŸ¦‹ SYNOPSIS πŸ¦‹

so first things first, bestie: to compile KCOV into the kernel, you gotta place these lines in your kernel configuration file:

options COVERAGE
options KCOV

and the header file that defines the application interface provided by KCOV is this one right here:

#include <sys/kcov.h>

πŸ‘‘ DESCRIPTION πŸ‘‘

kcov is a module that enables collection of code coverage information from the kernel. it literally relies on code instrumentation enabled by the COVERAGE kernel option. like, that's the whole vibe check β€” no COVERAGE, no party. πŸ™ˆ

when kcov is enabled by a user-mode thread, it collects coverage information only for that thread, excluding hard interrupt handlers. and because of that, kcov is not suited to collect comprehensive coverage data for the entire kernel β€” its main purpose is to provide input for coverage-guided system call fuzzers. so it's a very specific queen with a very specific job. πŸ‘‘

in typical usage, a user-mode thread first allocates a chunk of memory to be shared with kcov. then the thread enables coverage tracing, with coverage data being written by the kernel to the shared memory region. when tracing is disabled, the kernel relinquishes its access to the shared memory region, and the written coverage data may be consumed. it's giving "handing the aux cord back" energy.

the shared memory buffer can be treated as a 64-bit unsigned integer followed by an array of records. the integer records the number of valid records and is updated by the kernel as coverage information is recorded. the state of the tracing buffer can be reset by writing the value 0 to this field. the record layout depends on the tracing mode set using the KIOENABLE ioctl.

two tracing modes are implemented β€” KCOV_MODE_TRACE_PC and KCOV_MODE_TRACE_CMP. let's break them down:

  • KCOV_MODE_TRACE_PC β€” PC-tracing records a program counter value for each basic block executed while tracing is enabled. in this mode, each record is a single 64-bit unsigned integer containing the program counter value. simple, clean, iconic.
  • KCOV_MODE_TRACE_CMP β€” comparison tracing provides information about data flow; information about dynamic variable comparisons is recorded. such records provide information about the results of C if or switch statements, for example (that's c(7) in the original, bestie πŸ’–).

in comparison-tracing mode, each record consists of four 64-bit unsigned integers. the first integer is a bitmask defining attributes of the variables involved in the comparison. KCOV_CMP_CONST is set if one of the variables has a constant value at compile-time, and KCOV_CMP_SIZE(n) specifies the width of the variables:

KCOV_CMP_SIZE(0): a comparison of 8-bit integers
KCOV_CMP_SIZE(1): a comparison of 16-bit integers
KCOV_CMP_SIZE(2): a comparison of 32-bit integers
KCOV_CMP_SIZE(3): a comparison of 64-bit integers

the second and third fields record the values of the two variables, and the fourth and final field stores the program counter value of the comparison. four fields, all doing their thing, all main character energy. ✨


πŸ”₯ IOCTL INTERFACE πŸ”₯

applications interact with kcov using the ioctl(2) system call. heads up though: each thread making use of kcov must use a separate file descriptor for /dev/kcov. no sharing, that's the rule. 🫢

the following ioctls are defined:

  • KIOSETBUFSIZE size_t entries β€” set the size of the tracing buffer in units of KCOV_ENTRY_SIZE. the buffer may then be mapped into the calling thread's address space by calling mmap(2) on the kcov device file.
  • KIOENABLE int mode β€” enable coverage tracing for the calling thread. valid modes are KCOV_MODE_TRACE_PC and KCOV_MODE_TRACE_CMP.
  • KIODISABLE β€” disable coverage tracing for the calling thread. that's it. she's done.

πŸŽ€ FILES πŸŽ€

kcov creates the /dev/kcov device file. iconic of her honestly.


🌈 EXAMPLES 🌈

the following code sample collects information about basic block coverage for kernel code executed while printing β€˜Hello, world’.

size_t sz;
uint64_t *buf;
int fd;

fd = open("/dev/kcov", O_RDWR);
if (fd == -1)
        err(1, "open(/dev/kcov)");
sz = 1ul << 20; /* 1MB */
if (ioctl(fd, KIOSETBUFSIZE, sz / KCOV_ENTRY_SIZE) != 0)
        err(1, "ioctl(KIOSETBUFSIZE)");
buf = mmap(NULL, sz, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (buf == MAP_FAILED)
        err(1, "mmap");

/* Enable PC tracing. */
if (ioctl(fd, KIOENABLE, KCOV_MODE_TRACE_PC) != 0)
        err(1, "ioctl(KIOENABLE)");

/* Clear trace records from the preceding ioctl() call. */
buf[0] = 0;

printf("Hello, world!\n");

/* Disable PC tracing. */
if (ioctl(fd, KIODISABLE, 0) != 0)
        err(1, "ioctl(KIODISABLE)");

for (uint64_t i = 1; i < buf[0]; i++)
        printf("%#jx\n", (uintmax_t)buf[i]);

the output of this program can be approximately mapped to line numbers in kernel source code:

# ./kcov-test | sed 1d | addr2line -e /usr/lib/debug/boot/kernel/kernel.debug

like, look at that pipeline. sed 1d? addr2line? she ate. πŸ’…


πŸ’œ SEE ALSO πŸ’œ


⭐ HISTORY ⭐

kcov first appeared in FreeBSD 13.0. a whole era.


πŸ’€ BUGS πŸ’€

the FreeBSD implementation of kcov does not yet support remote tracing. so if that's what you were hoping for... not yet, bestie. 😭


ok that's the whole page!! go instrument some kernel code and collect that coverage data, you absolute legend 🧚✨

this page is from the Debian edition, dated August 18, 2020 β€” mwah, thanks for reading, love u, bye! πŸ’–πŸŒΈπŸ¦‹

πŸ“¦ where this page comes from