Skip to content
Permalink
Browse files

kcov: collect coverage from remote threads

  • Loading branch information...
xairy committed Jun 1, 2018
1 parent 700fb65 commit ff543afbf78902acea566fa4c635240ede651f77
Showing with 493 additions and 31 deletions.
  1. +81 −0 Documentation/dev-tools/kcov.rst
  2. +4 −0 drivers/usb/core/hub.c
  3. +6 −0 include/linux/kcov.h
  4. +3 −0 include/linux/sched.h
  5. +18 −0 include/uapi/linux/kcov.h
  6. +381 −31 kernel/kcov.c
@@ -202,3 +202,84 @@ 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 and KCOV_REMOTE_TRACK it's possible to collect coverage
from arbitrary kernel threads. For that the targeted section of code needs to
be annotated with kcov_remote_start(unique_id) and kcov_remote_stop(). Then
ioctl(kcov, KCOV_REMOTE_TRACK, unique_id) can be used to make kcov start
collecting coverage from that code section. Multiple ids can be targeted with
the same kcov device simultaneously.

.. code-block:: c

struct kcov_remote_arg {
unsigned trace_mode;
unsigned area_size;
unsigned num_handles;
unsigned unused;
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;
}
@@ -5197,6 +5197,8 @@ static void hub_event(struct work_struct *work)
hub_dev = hub->intfdev;
intf = to_usb_interface(hub_dev);

kcov_remote_start(kcov_remote_handle_usb(hdev->bus->busnum));

dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x\n",
hdev->state, hdev->maxchild,
/* NOTE: expects max 15 ports... */
@@ -5303,6 +5305,8 @@ static void hub_event(struct work_struct *work)
/* Balance the stuff in kick_hub_wq() and allow autosuspend */
usb_autopm_put_interface(intf);
kref_put(&hub->kref, hub_release);

kcov_remote_stop();
}

static const struct usb_device_id hub_id_table[] = {
@@ -25,10 +25,16 @@ 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);

#else

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) {}

#endif /* CONFIG_KCOV */
#endif /* _LINUX_KCOV_H */
@@ -1126,6 +1126,9 @@ 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;
#endif
@@ -4,9 +4,20 @@

#include <linux/types.h>

struct kcov_remote_arg {
unsigned trace_mode;
unsigned area_size;
unsigned num_handles;
unsigned unused;
__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 {
/*
@@ -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 bus)
{
return KCOV_REMOTE_HANDLE_USB + (__u64)bus;
}

#endif /* _LINUX_KCOV_IOCTLS_H */

0 comments on commit ff543af

Please sign in to comment.
You can’t perform that action at this time.