@@ -40,11 +40,12 @@ Coverage data only becomes accessible once debugfs has been mounted::
mount -t debugfs none /sys/kernel/debug
-Coverage collection
+Coverage collection for different modes
-------------------
The following program demonstrates how to use KCOV to collect coverage for a
-single syscall from within a test program:
+single syscall from within a test program, argv[1] can be provided to select
+which mode to enable:
.. code-block:: c
@@ -60,55 +61,130 @@ single syscall from within a test program:
#include <fcntl.h>
#include <linux/types.h>
- #define KCOV_INIT_TRACE _IOR('c', 1, unsigned long)
+ #define KCOV_INIT_TRACE _IOR('c', 1, unsigned long)
#define KCOV_ENABLE _IO('c', 100)
- #define KCOV_DISABLE _IO('c', 101)
+ #define KCOV_DISABLE _IO('c', 101)
#define COVER_SIZE (64<<10)
#define KCOV_TRACE_PC 0
#define KCOV_TRACE_CMP 1
+ #define KCOV_TRACE_UNIQ_PC 2
+ #define KCOV_TRACE_UNIQ_EDGE 4
+ #define KCOV_TRACE_UNIQ_CMP 8
+
+ /* Number of 64-bit words per record. */
+ #define KCOV_WORDS_PER_CMP 4
+
+ /*
+ * The format for the types of collected comparisons.
+ *
+ * Bit 0 shows whether one of the arguments is a compile-time constant.
+ * Bits 1 & 2 contain log2 of the argument size, up to 8 bytes.
+ */
+
+ #define KCOV_CMP_CONST (1 << 0)
+ #define KCOV_CMP_SIZE(n) ((n) << 1)
+ #define KCOV_CMP_MASK KCOV_CMP_SIZE(3)
int main(int argc, char **argv)
{
- int fd;
- unsigned long *cover, n, i;
-
- /* A single fd descriptor allows coverage collection on a single
- * thread.
- */
- fd = open("/sys/kernel/debug/kcov", O_RDWR);
- if (fd == -1)
- perror("open"), exit(1);
- /* Setup trace mode and trace size. */
- if (ioctl(fd, KCOV_INIT_TRACE, COVER_SIZE))
- perror("ioctl"), exit(1);
- /* Mmap buffer shared between kernel- and user-space. */
- cover = (unsigned long*)mmap(NULL, COVER_SIZE * sizeof(unsigned long),
- PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
- if ((void*)cover == MAP_FAILED)
- perror("mmap"), exit(1);
- /* Enable coverage collection on the current thread. */
- if (ioctl(fd, KCOV_ENABLE, KCOV_TRACE_PC))
- perror("ioctl"), exit(1);
- /* Reset coverage from the tail of the ioctl() call. */
- __atomic_store_n(&cover[0], 0, __ATOMIC_RELAXED);
- /* Call the target syscall call. */
- read(-1, NULL, 0);
- /* Read number of PCs collected. */
- n = __atomic_load_n(&cover[0], __ATOMIC_RELAXED);
- for (i = 0; i < n; i++)
- printf("0x%lx\n", cover[i + 1]);
- /* Disable coverage collection for the current thread. After this call
- * coverage can be enabled for a different thread.
- */
- if (ioctl(fd, KCOV_DISABLE, 0))
- perror("ioctl"), exit(1);
- /* Free resources. */
- if (munmap(cover, COVER_SIZE * sizeof(unsigned long)))
- perror("munmap"), exit(1);
- if (close(fd))
- perror("close"), exit(1);
- return 0;
+ int fd;
+ unsigned long *cover, *edge, n, n1, i, type, arg1, arg2, is_const, size;
+ unsigned int mode = KCOV_TRACE_PC;
+
+ /* argv[1] controls which mode to use, default to KCOV_TRACE_PC.
+ * supported modes include:
+ * KCOV_TRACE_PC
+ * KCOV_TRACE_CMP
+ * KCOV_TRACE_UNIQ_PC
+ * KCOV_TRACE_UNIQ_EDGE
+ * KCOV_TRACE_UNIQ_PC | KCOV_TRACE_UNIQ_EDGE
+ * KCOV_TRACE_UNIQ_CMP
+ */
+ if (argc > 1)
+ mode = (unsigned int)strtoul(argv[1], NULL, 10);
+ printf("The mode is: %u\n", mode);
+ if (mode != KCOV_TRACE_PC && mode != KCOV_TRACE_CMP &&
+ !(mode & (KCOV_TRACE_UNIQ_PC | KCOV_TRACE_UNIQ_EDGE | KCOV_TRACE_UNIQ_CMP))) {
+ printf("Unsupported mode!\n");
+ exit(1);
+ }
+ /* A single fd descriptor allows coverage collection on a single
+ * thread.
+ */
+ fd = open("/sys/kernel/debug/kcov", O_RDWR);
+ if (fd == -1)
+ perror("open"), exit(1);
+ /* Setup trace mode and trace size. */
+ if (ioctl(fd, KCOV_INIT_TRACE, COVER_SIZE))
+ perror("ioctl"), exit(1);
+ /* Mmap buffer shared between kernel- and user-space. */
+ cover = (unsigned long*)mmap(NULL, COVER_SIZE * sizeof(unsigned long),
+ PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if ((void*)cover == MAP_FAILED)
+ perror("mmap"), exit(1);
+ if (mode & KCOV_TRACE_UNIQ_EDGE) {
+ edge = (unsigned long*)mmap(NULL, COVER_SIZE * sizeof(unsigned long),
+ PROT_READ | PROT_WRITE, MAP_SHARED, fd, COVER_SIZE * sizeof(unsigned long));
+ if ((void*)edge == MAP_FAILED)
+ perror("mmap"), exit(1);
+ }
+ /* Enable coverage collection on the current thread. */
+ if (ioctl(fd, KCOV_ENABLE, mode))
+ perror("ioctl"), exit(1);
+ /* Reset coverage from the tail of the ioctl() call. */
+ __atomic_store_n(&cover[0], 0, __ATOMIC_RELAXED);
+ if (mode & KCOV_TRACE_UNIQ_EDGE)
+ __atomic_store_n(&edge[0], 0, __ATOMIC_RELAXED);
+ /* Call the target syscall call. */
+ read(-1, NULL, 0);
+ /* Read number of PCs collected. */
+ n = __atomic_load_n(&cover[0], __ATOMIC_RELAXED);
+ if (mode & KCOV_TRACE_UNIQ_EDGE)
+ n1 = __atomic_load_n(&edge[0], __ATOMIC_RELAXED);
+ if (mode & (KCOV_TRACE_CMP | KCOV_TRACE_UNIQ_CMP)) {
+ for (i = 0; i < n; i++) {
+ uint64_t ip;
+
+ type = cover[i * KCOV_WORDS_PER_CMP + 1];
+ /* arg1 and arg2 - operands of the comparison. */
+ arg1 = cover[i * KCOV_WORDS_PER_CMP + 2];
+ arg2 = cover[i * KCOV_WORDS_PER_CMP + 3];
+ /* ip - caller address. */
+ ip = cover[i * KCOV_WORDS_PER_CMP + 4];
+ /* size of the operands. */
+ size = 1 << ((type & KCOV_CMP_MASK) >> 1);
+ /* is_const - true if either operand is a compile-time constant.*/
+ is_const = type & KCOV_CMP_CONST;
+ printf("ip: 0x%lx type: 0x%lx, arg1: 0x%lx, arg2: 0x%lx, "
+ "size: %lu, %s\n",
+ ip, type, arg1, arg2, size,
+ is_const ? "const" : "non-const");
+ }
+ } else {
+ for (i = 0; i < n; i++)
+ printf("0x%lx\n", cover[i + 1]);
+ if (mode & KCOV_TRACE_UNIQ_EDGE) {
+ printf("======edge======\n");
+ for (i = 0; i < n1; i++)
+ printf("0x%lx\n", edge[i + 1]);
+ }
+ }
+ /* Disable coverage collection for the current thread. After this call
+ * coverage can be enabled for a different thread.
+ */
+ if (ioctl(fd, KCOV_DISABLE, 0))
+ perror("ioctl"), exit(1);
+ /* Free resources. */
+ if (munmap(cover, COVER_SIZE * sizeof(unsigned long)))
+ perror("munmap"), exit(1);
+ if (mode & KCOV_TRACE_UNIQ_EDGE) {
+ if (munmap(edge, COVER_SIZE * sizeof(unsigned long)))
+ perror("munmap"), exit(1);
+ }
+ if (close(fd))
+ perror("close"), exit(1);
+ return 0;
}
After piping through ``addr2line`` the output of the program looks as follows::
@@ -137,85 +213,10 @@ mmaps coverage buffer, and then forks child processes in a loop. The child
processes only need to enable coverage (it gets disabled automatically when
a thread exits).
-Comparison operands collection
-------------------------------
-
-Comparison operands collection is similar to coverage collection:
-
-.. code-block:: c
-
- /* Same includes and defines as above. */
-
- /* Number of 64-bit words per record. */
- #define KCOV_WORDS_PER_CMP 4
-
- /*
- * The format for the types of collected comparisons.
- *
- * Bit 0 shows whether one of the arguments is a compile-time constant.
- * Bits 1 & 2 contain log2 of the argument size, up to 8 bytes.
- */
-
- #define KCOV_CMP_CONST (1 << 0)
- #define KCOV_CMP_SIZE(n) ((n) << 1)
- #define KCOV_CMP_MASK KCOV_CMP_SIZE(3)
-
- int main(int argc, char **argv)
- {
- int fd;
- uint64_t *cover, type, arg1, arg2, is_const, size;
- unsigned long n, i;
-
- fd = open("/sys/kernel/debug/kcov", O_RDWR);
- if (fd == -1)
- perror("open"), exit(1);
- if (ioctl(fd, KCOV_INIT_TRACE, COVER_SIZE))
- perror("ioctl"), exit(1);
- /*
- * Note that the buffer pointer is of type uint64_t*, because all
- * the comparison operands are promoted to uint64_t.
- */
- cover = (uint64_t *)mmap(NULL, COVER_SIZE * sizeof(unsigned long),
- PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
- if ((void*)cover == MAP_FAILED)
- perror("mmap"), exit(1);
- /* Note KCOV_TRACE_CMP instead of KCOV_TRACE_PC. */
- if (ioctl(fd, KCOV_ENABLE, KCOV_TRACE_CMP))
- perror("ioctl"), exit(1);
- __atomic_store_n(&cover[0], 0, __ATOMIC_RELAXED);
- read(-1, NULL, 0);
- /* Read number of comparisons collected. */
- n = __atomic_load_n(&cover[0], __ATOMIC_RELAXED);
- for (i = 0; i < n; i++) {
- uint64_t ip;
-
- type = cover[i * KCOV_WORDS_PER_CMP + 1];
- /* arg1 and arg2 - operands of the comparison. */
- arg1 = cover[i * KCOV_WORDS_PER_CMP + 2];
- arg2 = cover[i * KCOV_WORDS_PER_CMP + 3];
- /* ip - caller address. */
- ip = cover[i * KCOV_WORDS_PER_CMP + 4];
- /* size of the operands. */
- size = 1 << ((type & KCOV_CMP_MASK) >> 1);
- /* is_const - true if either operand is a compile-time constant.*/
- is_const = type & KCOV_CMP_CONST;
- printf("ip: 0x%lx type: 0x%lx, arg1: 0x%lx, arg2: 0x%lx, "
- "size: %lu, %s\n",
- ip, type, arg1, arg2, size,
- is_const ? "const" : "non-const");
- }
- if (ioctl(fd, KCOV_DISABLE, 0))
- perror("ioctl"), exit(1);
- /* Free resources. */
- if (munmap(cover, COVER_SIZE * sizeof(unsigned long)))
- perror("munmap"), exit(1);
- if (close(fd))
- perror("close"), exit(1);
- return 0;
- }
-
Note that the KCOV modes (collection of code coverage or comparison operands)
-are mutually exclusive.
+are mutually exclusive, KCOV_TRACE_UNIQ_PC and KCOV_TRACE_UNIQ_EDGE can be
+enabled together.
+
Remote coverage collection
--------------------------
- Use single program to select different mode. - Mode [0|1|2|4|8] to KCOV_TRACE_[PC|CMP|UNIQ_PC|UNIQ_EDGE|UNIQ_CMP]. - Mode 6 to KCOV_TRACE_UNIQ_PC and KCOV_TRACE_UNIQ_EDGE. Signed-off-by: Jiao, Joey <quic_jiangenj@quicinc.com> --- Documentation/dev-tools/kcov.rst | 243 ++++++++++++++++++++------------------- 1 file changed, 122 insertions(+), 121 deletions(-)