Training courses

Kernel and Embedded Linux

Bootlin training courses

Embedded Linux, kernel,
Yocto Project, Buildroot, real-time,
graphics, boot time, debugging...

Bootlin logo

Elixir Cross Referencer

.\"	$NetBSD: kcov.4,v 1.6 2019/05/28 21:31:53 kamil Exp $
.\"
.\" Copyright (c) 2018 Anton Lindqvist <anton@openbsd.org>
.\"
.\" Permission to use, copy, modify, and distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above
.\" copyright notice and this permission notice appear in all copies.
.\"
.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.Dd May 28, 2019
.Dt KCOV 4
.Os
.Sh NAME
.Nm kcov
.Nd kernel code coverage tracing
.Sh SYNOPSIS
.Cd options KCOV
.Pp
.In sys/kcov.h
.Sh DESCRIPTION
The
.Nm
driver implements collection of code coverage inside the kernel.
It can be enabled on a per thread basis from userland,
allowing the kernel program counter to be collected during syscalls triggered by
the same thread.
.Pp
The
.Nm
descriptors (KD) are allocated during
.Xr open 2 ,
and are associated with a file descriptor.
A thread can enable the
.Nm
device.
When this happens,
this thread becomes the owner of the
.Nm
descriptors (KD),
and no thread can disable this KD except the owner.
.Pp
A
.Nm
descriptor (KD)
is freed when its file descriptor is closed iff the KD is not active on a thread.
If it is,
we ask the thread to free it when it exits.
.Pp
The collected coverage can be accessed by mapping the device
using
.Xr mmap 2 .
The buffers are mapped without risk that the kernel frees a buffer still mapped in a process.
.Pp
By default,
.Nm
is not enabled but requires the compile-time configuration
.Cd makeoptions KCOV
.Cd options KCOV
to be present,
see
.Xr options 4 .
.Pp
The following
.Xr ioctl 2
calls are provided:
.Bl -tag -width 4n
.It Dv KCOV_IOC_SETBUFSIZE Fa uint64_t *nentries
Allocate a coverage buffer with a capacity of
.Fa nentries .
The buffer can be accessed using
.Xr mmap 2
whereas the returned pointer must be interpreted as an array of
.Vt kcov_int_t
entries.
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 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
.Sh FILES
.Bl -tag -width /dev/kcov -compact
.It Pa /dev/kcov
Default device node.
.El
.Sh EXAMPLES
In the following example,
the
.Xr read 2
syscall is traced and the coverage displayed, which in turn can be passed to
.Xr addr2line 1
in order to translate the kernel program counter into the file name and line
number it corresponds to.
.Bd -literal
#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <sys/ioccom.h>
#include <sys/ioctl.h>
#include <sys/mman.h>

#include <sys/kcov.h>

int
main(void)
{
	kcov_int_t *cover, i, n;
	uint64_t size = 1024 * 100;
	int fd;
	int mode;

	fd = open("/dev/kcov", O_RDWR);
	if (fd == -1)
		err(1, "open");
	if (ioctl(fd, KCOV_IOC_SETBUFSIZE, &size) == -1)
		err(1, "ioctl: KCOV_IOC_SETBUFSIZE");
	cover = mmap(NULL, size * KCOV_ENTRY_SIZE,
	    PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
	if (cover == MAP_FAILED)
		err(1, "mmap");
	mode = KCOV_MODE_TRACE_PC;
	if (ioctl(fd, KCOV_IOC_ENABLE, &mode) == -1)
		err(1, "ioctl: KCOV_IOC_ENABLE");
	cover[0] = 0;
	read(-1, NULL, 0); /* syscall paths to be traced */
	n = cover[0];
	if (ioctl(fd, KCOV_IOC_DISABLE) == -1)
		err(1, "ioctl: KCOV_IOC_DISABLE");
	for (i = 0; i < n; i++)
		printf("%p\en", (void *)cover[i + 1]);
	if (munmap(cover, size * KCOV_ENTRY_SIZE) == -1)
		err(1, "munmap");
	close(fd);

	return 0;
}
.Ed
.Sh SEE ALSO
.Xr options 4
.Sh HISTORY
The
.Nm
driver was initially developed in Linux.
A driver based on the same concept was then implemented in
.Nx 9 .
.Sh AUTHORS
.An Siddharth Muralee Aq Mt siddharth.muralee@gmail.com