1.\" $NetBSD: kcov.4,v 1.6 2019/05/28 21:31:53 kamil Exp $ 2.\" 3.\" Copyright (c) 2018 Anton Lindqvist <anton@openbsd.org> 4.\" 5.\" Permission to use, copy, modify, and distribute this software for any 6.\" purpose with or without fee is hereby granted, provided that the above 7.\" copyright notice and this permission notice appear in all copies. 8.\" 9.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16.\" 17.Dd May 28, 2019 18.Dt KCOV 4 19.Os 20.Sh NAME 21.Nm kcov 22.Nd kernel code coverage tracing 23.Sh SYNOPSIS 24.Cd options KCOV 25.Pp 26.In sys/kcov.h 27.Sh DESCRIPTION 28The 29.Nm 30driver implements collection of code coverage inside the kernel. 31It can be enabled on a per thread basis from userland, 32allowing the kernel program counter to be collected during syscalls triggered by 33the same thread. 34.Pp 35The 36.Nm 37descriptors (KD) are allocated during 38.Xr open 2 , 39and are associated with a file descriptor. 40A thread can enable the 41.Nm 42device. 43When this happens, 44this thread becomes the owner of the 45.Nm 46descriptors (KD), 47and no thread can disable this KD except the owner. 48.Pp 49A 50.Nm 51descriptor (KD) 52is freed when its file descriptor is closed iff the KD is not active on a thread. 53If it is, 54we ask the thread to free it when it exits. 55.Pp 56The collected coverage can be accessed by mapping the device 57using 58.Xr mmap 2 . 59The buffers are mapped without risk that the kernel frees a buffer still mapped in a process. 60.Pp 61By default, 62.Nm 63is not enabled but requires the compile-time configuration 64.Cd makeoptions KCOV 65.Cd options KCOV 66to be present, 67see 68.Xr options 4 . 69.Pp 70The following 71.Xr ioctl 2 72calls are provided: 73.Bl -tag -width 4n 74.It Dv KCOV_IOC_SETBUFSIZE Fa uint64_t *nentries 75Allocate a coverage buffer with a capacity of 76.Fa nentries . 77The buffer can be accessed using 78.Xr mmap 2 79whereas the returned pointer must be interpreted as an array of 80.Vt kcov_int_t 81entries. 82Note that kcov_int_t is volatile. 83The first entry contains the number of entries in the array, 84excluding the first entry. 85.It Dv KCOV_IOC_ENABLE Fa int *mode 86Enable code coverage tracing for the current thread. 87The 88.Fa mode 89must be one of the following: 90.Bl -ohang 91.It Dv KCOV_MODE_NONE 92No trace selected. 93This option is useful for testing the 94.Nm 95device. 96.It Dv KCOV_MODE_TRACE_PC 97Trace the kernel program counter. 98.It Dv KCOV_MODE_TRACE_CMP 99Trace comparison instructions and switch statements. 100For switch statements, the number of traced comparison instructions is equal to 101the number of switch cases. 102Each traced comparison instruction is represented by 4 entries in the coverage 103buffer: 104.Bl -enum 105.It 106A mask where the least significant bit is set if one of the comparison operands 107is a compile-time constant, which is always true for switch statements. 108The remaining bits represents the log2 size of the operands, ranging from 0 to 1093. 110.It 111First comparison operand. 112For switch statements, this operand corresponds to the case value. 113.It 114Second comparison operand. 115For switch statements, this operand corresponds to the value passed to switch. 116.It 117Kernel program counter where the comparison instruction took place. 118.El 119.Pp 120In this mode, the first entry in the coverage buffer reflects the number of 121traced comparison instructions. 122Thus, the effective number of entries in the coverage buffer is given by 123multiplying the first entry by 4. 124.El 125.It Dv KCOV_IOC_DISABLE Fa void 126Disable code coverage tracing for the current thread. 127.El 128.Sh FILES 129.Bl -tag -width /dev/kcov -compact 130.It Pa /dev/kcov 131Default device node. 132.El 133.Sh EXAMPLES 134In the following example, 135the 136.Xr read 2 137syscall is traced and the coverage displayed, which in turn can be passed to 138.Xr addr2line 1 139in order to translate the kernel program counter into the file name and line 140number it corresponds to. 141.Bd -literal 142#include <err.h> 143#include <fcntl.h> 144#include <stdio.h> 145#include <stdlib.h> 146#include <unistd.h> 147 148#include <sys/ioccom.h> 149#include <sys/ioctl.h> 150#include <sys/mman.h> 151 152#include <sys/kcov.h> 153 154int 155main(void) 156{ 157 kcov_int_t *cover, i, n; 158 uint64_t size = 1024 * 100; 159 int fd; 160 int mode; 161 162 fd = open("/dev/kcov", O_RDWR); 163 if (fd == -1) 164 err(1, "open"); 165 if (ioctl(fd, KCOV_IOC_SETBUFSIZE, &size) == -1) 166 err(1, "ioctl: KCOV_IOC_SETBUFSIZE"); 167 cover = mmap(NULL, size * KCOV_ENTRY_SIZE, 168 PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 169 if (cover == MAP_FAILED) 170 err(1, "mmap"); 171 mode = KCOV_MODE_TRACE_PC; 172 if (ioctl(fd, KCOV_IOC_ENABLE, &mode) == -1) 173 err(1, "ioctl: KCOV_IOC_ENABLE"); 174 cover[0] = 0; 175 read(-1, NULL, 0); /* syscall paths to be traced */ 176 n = cover[0]; 177 if (ioctl(fd, KCOV_IOC_DISABLE) == -1) 178 err(1, "ioctl: KCOV_IOC_DISABLE"); 179 for (i = 0; i < n; i++) 180 printf("%p\en", (void *)cover[i + 1]); 181 if (munmap(cover, size * KCOV_ENTRY_SIZE) == -1) 182 err(1, "munmap"); 183 close(fd); 184 185 return 0; 186} 187.Ed 188.Sh SEE ALSO 189.Xr options 4 190.Sh HISTORY 191The 192.Nm 193driver was initially developed in Linux. 194A driver based on the same concept was then implemented in 195.Nx 9 . 196.Sh AUTHORS 197.An Siddharth Muralee Aq Mt siddharth.muralee@gmail.com 198