aboutsummaryrefslogtreecommitdiff
path: root/tests/sys/kern/kcov.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/sys/kern/kcov.c')
-rw-r--r--tests/sys/kern/kcov.c477
1 files changed, 477 insertions, 0 deletions
diff --git a/tests/sys/kern/kcov.c b/tests/sys/kern/kcov.c
new file mode 100644
index 000000000000..cecc0cda78ea
--- /dev/null
+++ b/tests/sys/kern/kcov.c
@@ -0,0 +1,477 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2018, 2019 Andrew Turner
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/kcov.h>
+#include <sys/mman.h>
+
+#include <machine/atomic.h>
+
+#include <fcntl.h>
+#include <pthread.h>
+#include <semaphore.h>
+
+#include <atf-c.h>
+
+static const char *modes[] = {
+ "PC tracing",
+ "comparison tracing",
+};
+
+static size_t page_size;
+
+static void
+init_page_size(void)
+{
+ page_size = getpagesize();
+}
+
+static int
+open_kcov(void)
+{
+ int fd;
+
+ fd = open("/dev/kcov", O_RDWR);
+ if (fd == -1)
+ atf_tc_skip("Failed to open /dev/kcov");
+
+ return (fd);
+}
+
+ATF_TC(kcov_bufsize);
+ATF_TC_HEAD(kcov_bufsize, tc)
+{
+ init_page_size();
+}
+
+ATF_TC_BODY(kcov_bufsize, tc)
+{
+ int fd;
+
+ fd = open_kcov();
+
+ ATF_CHECK(ioctl(fd, KIOSETBUFSIZE, 0) == -1);
+ ATF_CHECK(ioctl(fd, KIOSETBUFSIZE, 1) == -1);
+ ATF_CHECK(ioctl(fd, KIOSETBUFSIZE, 2) == 0);
+ ATF_CHECK(ioctl(fd, KIOSETBUFSIZE, 2) == -1);
+
+ close(fd);
+}
+
+ATF_TC(kcov_mmap);
+ATF_TC_HEAD(kcov_mmap, tc)
+{
+ init_page_size();
+}
+
+ATF_TC_BODY(kcov_mmap, tc)
+{
+ void *data1, *data2;
+ int fd;
+
+ fd = open_kcov();
+
+ ATF_CHECK(mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED,
+ fd, 0) == MAP_FAILED);
+
+ ATF_REQUIRE(ioctl(fd, KIOSETBUFSIZE,
+ 2 * page_size / KCOV_ENTRY_SIZE) == 0);
+
+ ATF_CHECK(mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED,
+ fd, 0) == MAP_FAILED);
+ ATF_CHECK(mmap(NULL, 3 * page_size, PROT_READ | PROT_WRITE, MAP_SHARED,
+ fd, 0) == MAP_FAILED);
+ ATF_REQUIRE((data1 = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
+ MAP_SHARED, fd, 0)) != MAP_FAILED);
+ ATF_REQUIRE((data2 = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
+ MAP_SHARED, fd, 0)) != MAP_FAILED);
+
+ *(uint64_t *)data1 = 0x123456789abcdeful;
+ ATF_REQUIRE(*(uint64_t *)data2 == 0x123456789abcdefull);
+ *(uint64_t *)data2 = 0xfedcba9876543210ul;
+ ATF_REQUIRE(*(uint64_t *)data1 == 0xfedcba9876543210ull);
+
+ munmap(data1, 2 * page_size);
+ munmap(data2, 2 * page_size);
+
+ close(fd);
+}
+
+/* This shouldn't panic */
+ATF_TC(kcov_mmap_no_munmap);
+ATF_TC_HEAD(kcov_mmap_no_munmap, tc)
+{
+ init_page_size();
+}
+
+ATF_TC_BODY(kcov_mmap_no_munmap, tc)
+{
+ int fd;
+
+ fd = open_kcov();
+
+ ATF_REQUIRE(ioctl(fd, KIOSETBUFSIZE, page_size / KCOV_ENTRY_SIZE) == 0);
+
+ ATF_CHECK(mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED,
+ fd, 0) != MAP_FAILED);
+
+ close(fd);
+}
+
+ATF_TC(kcov_mmap_no_munmap_no_close);
+ATF_TC_HEAD(kcov_mmap_no_munmap_no_close, tc)
+{
+ init_page_size();
+}
+
+ATF_TC_BODY(kcov_mmap_no_munmap_no_close, tc)
+{
+ int fd;
+
+ fd = open_kcov();
+
+ ATF_REQUIRE(ioctl(fd, KIOSETBUFSIZE, page_size / KCOV_ENTRY_SIZE) == 0);
+
+ ATF_CHECK(mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED,
+ fd, 0) != MAP_FAILED);
+}
+
+static sem_t sem1, sem2;
+
+static void *
+kcov_mmap_enable_thread(void *data)
+{
+ int fd;
+
+ fd = open_kcov();
+ *(int *)data = fd;
+
+ ATF_REQUIRE(ioctl(fd, KIOSETBUFSIZE, page_size / KCOV_ENTRY_SIZE) == 0);
+ ATF_CHECK(mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED,
+ fd, 0) != MAP_FAILED);
+ ATF_CHECK(ioctl(fd, KIOENABLE, KCOV_MODE_TRACE_PC) == 0);
+
+ sem_post(&sem1);
+ sem_wait(&sem2);
+
+ return (NULL);
+}
+
+ATF_TC(kcov_mmap_enable_thread_close);
+ATF_TC_HEAD(kcov_mmap_enable_thread_close, tc)
+{
+ init_page_size();
+}
+
+ATF_TC_BODY(kcov_mmap_enable_thread_close, tc)
+{
+ pthread_t thread;
+ int fd;
+
+ sem_init(&sem1, 0, 0);
+ sem_init(&sem2, 0, 0);
+ pthread_create(&thread, NULL,
+ kcov_mmap_enable_thread, &fd);
+ sem_wait(&sem1);
+ close(fd);
+ sem_post(&sem2);
+ pthread_join(thread, NULL);
+}
+
+ATF_TC(kcov_enable);
+ATF_TC_HEAD(kcov_enable, tc)
+{
+ init_page_size();
+}
+
+ATF_TC_BODY(kcov_enable, tc)
+{
+ int fd;
+
+ fd = open_kcov();
+
+ ATF_CHECK(ioctl(fd, KIOENABLE, KCOV_MODE_TRACE_PC) == -1);
+
+ ATF_REQUIRE(ioctl(fd, KIOSETBUFSIZE, page_size / KCOV_ENTRY_SIZE) == 0);
+
+ /* We need to enable before disable */
+ ATF_CHECK(ioctl(fd, KIODISABLE, 0) == -1);
+
+ /* Check enabling works only with a valid trace method */
+ ATF_CHECK(ioctl(fd, KIOENABLE, -1) == -1);
+ ATF_CHECK(ioctl(fd, KIOENABLE, KCOV_MODE_TRACE_PC) == 0);
+ ATF_CHECK(ioctl(fd, KIOENABLE, KCOV_MODE_TRACE_PC) == -1);
+ ATF_CHECK(ioctl(fd, KIOENABLE, KCOV_MODE_TRACE_CMP) == -1);
+
+ /* Disable should only be called once */
+ ATF_CHECK(ioctl(fd, KIODISABLE, 0) == 0);
+ ATF_CHECK(ioctl(fd, KIODISABLE, 0) == -1);
+
+ /* Re-enabling should also work */
+ ATF_CHECK(ioctl(fd, KIOENABLE, KCOV_MODE_TRACE_CMP) == 0);
+ ATF_CHECK(ioctl(fd, KIODISABLE, 0) == 0);
+
+ close(fd);
+}
+
+ATF_TC(kcov_enable_no_disable);
+ATF_TC_HEAD(kcov_enable_no_disable, tc)
+{
+ init_page_size();
+}
+
+ATF_TC_BODY(kcov_enable_no_disable, tc)
+{
+ int fd;
+
+ fd = open_kcov();
+ ATF_REQUIRE(ioctl(fd, KIOSETBUFSIZE, page_size / KCOV_ENTRY_SIZE) == 0);
+ ATF_CHECK(ioctl(fd, KIOENABLE, KCOV_MODE_TRACE_PC) == 0);
+ close(fd);
+}
+
+ATF_TC(kcov_enable_no_disable_no_close);
+ATF_TC_HEAD(kcov_enable_no_disable_no_close, tc)
+{
+ init_page_size();
+}
+
+ATF_TC_BODY(kcov_enable_no_disable_no_close, tc)
+{
+ int fd;
+
+ fd = open_kcov();
+ ATF_REQUIRE(ioctl(fd, KIOSETBUFSIZE, page_size / KCOV_ENTRY_SIZE) == 0);
+ ATF_CHECK(ioctl(fd, KIOENABLE, KCOV_MODE_TRACE_PC) == 0);
+}
+
+static void *
+common_head(int *fdp)
+{
+ void *data;
+ int fd;
+
+ fd = open_kcov();
+
+ ATF_REQUIRE_MSG(ioctl(fd, KIOSETBUFSIZE,
+ page_size / KCOV_ENTRY_SIZE) == 0,
+ "Unable to set the kcov buffer size");
+
+ data = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ ATF_REQUIRE_MSG(data != MAP_FAILED, "Unable to mmap the kcov buffer");
+
+ *fdp = fd;
+ return (data);
+}
+
+static void
+common_tail(int fd, void *data)
+{
+
+ ATF_REQUIRE_MSG(munmap(data, page_size) == 0,
+ "Unable to unmap the kcov buffer");
+
+ close(fd);
+}
+
+static void
+basic_test(u_int mode)
+{
+ uint64_t *buf;
+ int fd;
+
+ buf = common_head(&fd);
+ ATF_REQUIRE_MSG(ioctl(fd, KIOENABLE, mode) == 0,
+ "Unable to enable kcov %s",
+ mode < nitems(modes) ? modes[mode] : "unknown mode");
+
+ atomic_store_64(&buf[0], 0);
+
+ sleep(0);
+ ATF_REQUIRE_MSG(atomic_load_64(&buf[0]) != 0, "No records found");
+
+ ATF_REQUIRE_MSG(ioctl(fd, KIODISABLE, 0) == 0,
+ "Unable to disable kcov");
+
+ common_tail(fd, buf);
+}
+
+ATF_TC(kcov_basic_pc);
+ATF_TC_HEAD(kcov_basic_pc, tc)
+{
+ init_page_size();
+}
+
+ATF_TC_BODY(kcov_basic_pc, tc)
+{
+ basic_test(KCOV_MODE_TRACE_PC);
+}
+
+ATF_TC(kcov_basic_cmp);
+ATF_TC_HEAD(kcov_basic_cmp, tc)
+{
+ init_page_size();
+}
+
+ATF_TC_BODY(kcov_basic_cmp, tc)
+{
+ basic_test(KCOV_MODE_TRACE_CMP);
+}
+
+static void *
+thread_test_helper(void *ptr)
+{
+ uint64_t *buf = ptr;
+
+ atomic_store_64(&buf[0], 0);
+ sleep(0);
+ ATF_REQUIRE_MSG(atomic_load_64(&buf[0]) == 0,
+ "Records changed in blocked thread");
+
+ return (NULL);
+}
+
+static void
+thread_test(u_int mode)
+{
+ pthread_t thread;
+ uint64_t *buf;
+ int fd;
+
+ buf = common_head(&fd);
+
+ ATF_REQUIRE_MSG(ioctl(fd, KIOENABLE, mode) == 0,
+ "Unable to enable kcov %s",
+ mode < nitems(modes) ? modes[mode] : "unknown mode");
+
+ pthread_create(&thread, NULL, thread_test_helper, buf);
+ pthread_join(thread, NULL);
+
+ ATF_REQUIRE_MSG(ioctl(fd, KIODISABLE, 0) == 0,
+ "Unable to disable kcov");
+
+ common_tail(fd, buf);
+}
+
+ATF_TC(kcov_thread_pc);
+ATF_TC_HEAD(kcov_thread_pc, tc)
+{
+ init_page_size();
+}
+
+ATF_TC_BODY(kcov_thread_pc, tc)
+{
+ thread_test(KCOV_MODE_TRACE_PC);
+}
+
+ATF_TC(kcov_thread_cmp);
+ATF_TC_HEAD(kcov_thread_cmp, tc)
+{
+ init_page_size();
+}
+
+ATF_TC_BODY(kcov_thread_cmp, tc)
+{
+ thread_test(KCOV_MODE_TRACE_CMP);
+}
+
+struct multi_thread_data {
+ uint64_t *buf;
+ int fd;
+ u_int mode;
+ int thread;
+};
+
+static void *
+multi_thread_test_helper(void *ptr)
+{
+ struct multi_thread_data *data = ptr;
+
+ ATF_REQUIRE_MSG(ioctl(data->fd, KIOENABLE, data->mode) == 0,
+ "Unable to enable kcov %s in thread %d",
+ data->mode < nitems(modes) ? modes[data->mode] : "unknown mode",
+ data->thread);
+
+ atomic_store_64(&data->buf[0], 0);
+ sleep(0);
+ ATF_REQUIRE_MSG(atomic_load_64(&data->buf[0]) != 0,
+ "No records found in thread %d", data->thread);
+
+ return (NULL);
+}
+
+ATF_TC(kcov_enable_multi_thread);
+ATF_TC_HEAD(kcov_enable_multi_thread, t)
+{
+ init_page_size();
+}
+
+ATF_TC_BODY(kcov_enable_multi_thread, t)
+{
+ struct multi_thread_data data;
+ pthread_t thread;
+
+ data.buf = common_head(&data.fd);
+
+ /* Run the thread to completion */
+ data.thread = 1;
+ data.mode = KCOV_MODE_TRACE_PC;
+ pthread_create(&thread, NULL, multi_thread_test_helper, &data);
+ pthread_join(thread, NULL);
+
+ /* Run it again to check enable works on the same fd */
+ data.thread = 2;
+ data.mode = KCOV_MODE_TRACE_CMP;
+ pthread_create(&thread, NULL, multi_thread_test_helper, &data);
+ pthread_join(thread, NULL);
+
+ common_tail(data.fd, data.buf);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, kcov_bufsize);
+ ATF_TP_ADD_TC(tp, kcov_mmap);
+ ATF_TP_ADD_TC(tp, kcov_mmap_no_munmap);
+ ATF_TP_ADD_TC(tp, kcov_mmap_no_munmap_no_close);
+ ATF_TP_ADD_TC(tp, kcov_enable);
+ ATF_TP_ADD_TC(tp, kcov_enable_no_disable);
+ ATF_TP_ADD_TC(tp, kcov_enable_no_disable_no_close);
+ ATF_TP_ADD_TC(tp, kcov_mmap_enable_thread_close);
+ ATF_TP_ADD_TC(tp, kcov_basic_pc);
+ ATF_TP_ADD_TC(tp, kcov_basic_cmp);
+ ATF_TP_ADD_TC(tp, kcov_thread_pc);
+ ATF_TP_ADD_TC(tp, kcov_thread_cmp);
+ ATF_TP_ADD_TC(tp, kcov_enable_multi_thread);
+ return (atf_no_error());
+}