Skip to content

Commit

Permalink
kcov: remote coverage support
Browse files Browse the repository at this point in the history
Signed-off-by: Andrey Konovalov <andreyknvl@google.com>
  • Loading branch information
xairy authored and ramosian-glider committed Jun 19, 2019
1 parent 7378830 commit 8017754
Show file tree
Hide file tree
Showing 5 changed files with 502 additions and 35 deletions.
100 changes: 100 additions & 0 deletions Documentation/dev-tools/kcov.rst
Expand Up @@ -34,6 +34,7 @@ Profiling data will only become accessible once debugfs has been mounted::

Coverage collection
-------------------

The following program demonstrates coverage collection from within a test
program using kcov:

Expand Down Expand Up @@ -128,6 +129,7 @@ only need to enable coverage (disable happens automatically on thread end).

Comparison operands collection
------------------------------

Comparison operands collection is similar to coverage collection:

.. code-block:: c
Expand Down Expand Up @@ -202,3 +204,101 @@ Comparison operands collection is similar to coverage collection:
Note that the kcov modes (coverage collection or comparison operands) are
mutually exclusive.

Remote coverage collection
--------------------------

With KCOV_REMOTE_ENABLE it's possible to collect coverage from arbitrary
kernel threads. For that the targeted section of kernel code needs to be
annotated with kcov_remote_start(unique_id) and kcov_remote_stop(). Then
ioctl(kcov, KCOV_REMOTE_ENABLE) can be used to make kcov start collecting
coverage from that code section. Multiple ids can be targeted with the same
kcov device simultaneously.

This allows to collect coverage from both types of kernel background threads:
the global ones, that are spawned during kernel boot and are always running
(e.g. USB hub_event); and the local ones, that are spawned when a user
interacts with some kernel interfaces (e.g. vhost). Collecting coverage from
both types of kernel threads requires custom annotations.

To collect coverage from a global background thread add kcov_remote_start/
kcov_remote_stop annotation with a unique id to that thread's code and then
pass this id in the handles array field of the kcov_remote_arg struct.

A tracking id for local background threads is passed through the common_handle
field of the kcov_remote_arg struct. This id gets saved to the kcov_handle
field in the thread that created kcov and needs to be passed to the newly
spawned threads via custom annotations. Those threads should be in turn
annotated with kcov_remote_start/kcov_remote_stop.

.. code-block:: c
struct kcov_remote_arg {
unsigned trace_mode;
unsigned area_size;
unsigned num_handles;
uint64_t common_handle;
uint64_t handles[0];
};
#define KCOV_REMOTE_MAX_HANDLES 0x10000
#define KCOV_INIT_TRACE _IOR('c', 1, unsigned long)
#define KCOV_ENABLE _IO('c', 100)
#define KCOV_DISABLE _IO('c', 101)
#define KCOV_REMOTE_ENABLE _IOW('c', 102, struct kcov_remote_arg)
#define KCOV_REMOTE_HANDLE_USB 0x4242000000000000ull
static inline __u64 kcov_remote_handle_usb(int bus)
{
return KCOV_REMOTE_HANDLE_USB + (__u64)bus;
}
#define COVER_SIZE (64 << 10)
#define KCOV_TRACE_PC 0
#define KCOV_TRACE_CMP 1
int main(int argc, char **argv)
{
int fd;
unsigned long *cover, n, i;
uint64_t handle;
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);
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 from the USB bus #1. */
arg = calloc(1, sizeof(*arg) + sizeof(uint64_t));
if (!arg)
perror("calloc"), exit(1);
arg->trace_mode = KCOV_TRACE_PC;
arg->area_size = COVER_SIZE;
arg->num_handles = 1;
arg->handles[0] = kcov_remote_handle_usb(1);
if (ioctl(fd, KCOV_REMOTE_ENABLE, arg))
perror("ioctl"), free(arg), exit(1);
free(arg);
/* Sleep. The user needs to trigger some activity on the USB bus #1. */
sleep(2);
n = __atomic_load_n(&cover[0], __ATOMIC_RELAXED);
for (i = 0; i < n; i++)
printf("0x%lx\n", cover[i + 1]);
if (ioctl(fd, KCOV_DISABLE, 0))
perror("ioctl"), exit(1);
if (munmap(cover, COVER_SIZE * sizeof(unsigned long)))
perror("munmap"), exit(1);
if (close(fd))
perror("close"), exit(1);
return 0;
}
5 changes: 5 additions & 0 deletions include/linux/kcov.h
Expand Up @@ -27,6 +27,9 @@ enum kcov_mode {
void kcov_task_init(struct task_struct *t);
void kcov_task_exit(struct task_struct *t);

void kcov_remote_start(u64 handle);
void kcov_remote_stop(void);

#define kcov_prepare_switch(t) \
do { \
(t)->kcov_mode |= KCOV_IN_CTXSW; \
Expand All @@ -41,6 +44,8 @@ do { \

static inline void kcov_task_init(struct task_struct *t) {}
static inline void kcov_task_exit(struct task_struct *t) {}
static inline void kcov_remote_start(u64 handle) {}
static inline void kcov_remote_stop(void) {}
static inline void kcov_prepare_switch(struct task_struct *t) {}
static inline void kcov_finish_switch(struct task_struct *t) {}

Expand Down
6 changes: 6 additions & 0 deletions include/linux/sched.h
Expand Up @@ -1151,8 +1151,14 @@ struct task_struct {
/* Buffer for coverage collection: */
void *kcov_area;

/* KCOV sequence number: */
int kcov_sequence;

/* KCOV descriptor wired with this task or NULL: */
struct kcov *kcov;

/* KCOV handle for remote coverage collection: */
u64 kcov_handle;
#endif

#ifdef CONFIG_MEMCG
Expand Down
18 changes: 18 additions & 0 deletions include/uapi/linux/kcov.h
Expand Up @@ -4,9 +4,20 @@

#include <linux/types.h>

struct kcov_remote_arg {
unsigned int trace_mode;
unsigned int area_size;
unsigned int num_handles;
__u64 common_handle;
__u64 handles[0];
};

#define KCOV_REMOTE_MAX_HANDLES 0x10000

#define KCOV_INIT_TRACE _IOR('c', 1, unsigned long)
#define KCOV_ENABLE _IO('c', 100)
#define KCOV_DISABLE _IO('c', 101)
#define KCOV_REMOTE_ENABLE _IOW('c', 102, struct kcov_remote_arg)

enum {
/*
Expand All @@ -32,4 +43,11 @@ enum {
#define KCOV_CMP_SIZE(n) ((n) << 1)
#define KCOV_CMP_MASK KCOV_CMP_SIZE(3)

#define KCOV_REMOTE_HANDLE_USB 0x4242000000000000ull

static inline __u64 kcov_remote_handle_usb(unsigned int bus)
{
return KCOV_REMOTE_HANDLE_USB + (__u64)bus;
}

#endif /* _LINUX_KCOV_IOCTLS_H */

0 comments on commit 8017754

Please sign in to comment.