[RFC 11/19] ktf: Add a small test suite with a few tests to test KTF itself

From: Knut Omang
Date: Tue Aug 13 2019 - 02:12:07 EST


context.c: Parameterized context test case, kernel side:
context.h: Parameterized context test case, kernel side.
context_self.h: The data structure passed between user level and kernel for the
hybrid.c: Hybrid (combined user level and kernel) self tests,
hybrid.h: Hybrid (combined user level and kernel) self tests,
hybrid_self.h: The data structure passed between user level and kernel for the
self.c: Some simple self tests for KTF

Signed-off-by: Knut Omang <knut.omang@xxxxxxxxxx>
---
tools/testing/selftests/ktf/selftest/Makefile | 17 +-
tools/testing/selftests/ktf/selftest/context.c | 149 +++-
tools/testing/selftests/ktf/selftest/context.h | 15 +-
tools/testing/selftests/ktf/selftest/context_self.h | 34 +-
tools/testing/selftests/ktf/selftest/hybrid.c | 35 +-
tools/testing/selftests/ktf/selftest/hybrid.h | 24 +-
tools/testing/selftests/ktf/selftest/hybrid_self.h | 27 +-
tools/testing/selftests/ktf/selftest/ktf_syms.txt | 17 +-
tools/testing/selftests/ktf/selftest/self.c | 661 +++++++++++++-
9 files changed, 979 insertions(+)
create mode 100644 tools/testing/selftests/ktf/selftest/Makefile
create mode 100644 tools/testing/selftests/ktf/selftest/context.c
create mode 100644 tools/testing/selftests/ktf/selftest/context.h
create mode 100644 tools/testing/selftests/ktf/selftest/context_self.h
create mode 100644 tools/testing/selftests/ktf/selftest/hybrid.c
create mode 100644 tools/testing/selftests/ktf/selftest/hybrid.h
create mode 100644 tools/testing/selftests/ktf/selftest/hybrid_self.h
create mode 100644 tools/testing/selftests/ktf/selftest/ktf_syms.txt
create mode 100644 tools/testing/selftests/ktf/selftest/self.c

diff --git a/tools/testing/selftests/ktf/selftest/Makefile b/tools/testing/selftests/ktf/selftest/Makefile
new file mode 100644
index 0000000..8737bf4
--- /dev/null
+++ b/tools/testing/selftests/ktf/selftest/Makefile
@@ -0,0 +1,17 @@
+# Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+#
+# SPDX-License-Identifier: GPL-2.0
+#
+# Kernel module implementing a test suite for testing KTF itself
+#
+
+ccflags-y += -Wno-vla
+
+ccflags-y += -I$(srctree)/$(src)/../kernel -I$(src)
+
+obj-m := selftest.o
+
+include $(srctree)/$(src)/../scripts/ktf_syms.mk
+
+selftest-y := self.o hybrid.o context.o
+
diff --git a/tools/testing/selftests/ktf/selftest/context.c b/tools/testing/selftests/ktf/selftest/context.c
new file mode 100644
index 0000000..9129b5b
--- /dev/null
+++ b/tools/testing/selftests/ktf/selftest/context.c
@@ -0,0 +1,149 @@
+/* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ *
+ * context.c: Parameterized context test case, kernel side:
+ */
+
+#include "ktf.h"
+#include "context.h"
+
+/* Declare a specific handle for this test to avoid interfering with the
+ * other tests:
+ */
+static KTF_HANDLE_INIT(ct_handle);
+
+struct param_test_ctx {
+ struct ktf_context k;
+ struct test_parameter_block p;
+};
+
+struct param_test_ctx param_ctx[2];
+
+#define MYVALUE 0xdabadaba
+
+/* Declare the callback that accepts a parameter block */
+static int param_ctx_cb(struct ktf_context *ctx, const void *data, size_t data_sz)
+{
+ struct param_test_ctx *px = container_of(ctx, struct param_test_ctx, k);
+ struct test_parameter_block *pb = (struct test_parameter_block *)data;
+ long orig_myvalue;
+
+ if (data_sz != sizeof(*pb))
+ return -EINVAL;
+ /* check data validity here, if possible.. */
+ orig_myvalue = px->p.myvalue;
+ memcpy(&px->p, pb, data_sz);
+ /* Enforce "policies" */
+ px->p.myvalue = orig_myvalue;
+ return 0;
+}
+
+TEST(selftest, param)
+{
+ struct param_test_ctx *px = container_of(ctx, struct param_test_ctx, k);
+
+ /* Now, here we can fail (using ASSERT) or ignore by silently return
+ * depending on what's most useful, if a test hasn't been configured.
+ * For this selftest we just use EXPECT so we can have the actual current
+ * parameter values reported as well.
+ *
+ * Notice that these parameters are
+ * persistent throughout the instance 'life' of the kernel test module,
+ * so if one user program has configured them, then
+ * programs ignorant of the parameters may still end up
+ * executing the tests with previously configured parameters:
+ *
+ * This simplified example uses the same configuration struct for both
+ * context type IDs, but the idea is that they can be completely different.
+ */
+ EXPECT_INT_EQ(ctx->config_errno, 0);
+ if (KTF_CONTEXT_CFG_OK(ctx)) {
+ switch (ctx->type->name[13]) {
+ case '1':
+ EXPECT_LONG_EQ(px->p.magic, CONTEXT_MAGIC1);
+ break;
+ case '2':
+ EXPECT_LONG_EQ(px->p.magic, CONTEXT_MAGIC2);
+ break;
+ case '3':
+ EXPECT_LONG_EQ(px->p.magic, CONTEXT_MAGIC3);
+ EXPECT_LONG_EQ(px->p.myvalue, MYVALUE);
+ break;
+ }
+ EXPECT_STREQ(px->p.s, CONTEXT_MSG);
+ } else {
+ EXPECT_LONG_EQ(px->p.magic, 0);
+ EXPECT_STREQ(px->p.s, "");
+ }
+}
+
+struct param_test_type {
+ struct ktf_context_type kt;
+ /* space for cfg data (such as constraints) for the context type */
+ long myvalue;
+};
+
+static struct ktf_context *type3_alloc(struct ktf_context_type *ct)
+{
+ struct param_test_type *pct = container_of(ct, struct param_test_type, kt);
+ struct param_test_ctx *ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+
+ ctx->p.myvalue = pct->myvalue;
+ return &ctx->k;
+}
+
+static void type3_cleanup(struct ktf_context *ctx)
+{
+ struct param_test_ctx *px = container_of(ctx, struct param_test_ctx, k);
+
+ kfree(px);
+}
+
+TEST(selftest, dupltype)
+{
+ /* Verify that we cannot add the same context type twice */
+
+ static struct param_test_type dupltype = {
+ .myvalue = 0,
+ .kt.alloc = type3_alloc,
+ .kt.config_cb = param_ctx_cb,
+ .kt.cleanup = type3_cleanup,
+ .kt.name = "context_type_3"
+ };
+
+ ASSERT_INT_EQ(-EEXIST, ktf_handle_add_ctx_type(&ct_handle, &dupltype.kt));
+}
+
+void add_context_tests(void)
+{
+ int ret = KTF_CONTEXT_ADD_TO_CFG(ct_handle, &param_ctx[0].k, "context1",
+ param_ctx_cb, "context_type_1");
+
+ if (ret)
+ return;
+
+ ret = KTF_CONTEXT_ADD_TO_CFG(ct_handle, &param_ctx[1].k, "context2",
+ param_ctx_cb, "context_type_2");
+ if (ret)
+ return;
+
+ {
+ static struct param_test_type ctx_type3 = {
+ .myvalue = MYVALUE,
+ .kt.alloc = type3_alloc,
+ .kt.config_cb = param_ctx_cb,
+ .kt.cleanup = type3_cleanup,
+ .kt.name = "context_type_3"
+ };
+ ret = ktf_handle_add_ctx_type(&ct_handle, &ctx_type3.kt);
+ }
+
+ ADD_TEST_TO(ct_handle, param);
+ ADD_TEST(dupltype);
+}
+
+void context_tests_cleanup(void)
+{
+ KTF_HANDLE_CLEANUP(ct_handle);
+}
diff --git a/tools/testing/selftests/ktf/selftest/context.h b/tools/testing/selftests/ktf/selftest/context.h
new file mode 100644
index 0000000..69b970a
--- /dev/null
+++ b/tools/testing/selftests/ktf/selftest/context.h
@@ -0,0 +1,15 @@
+/* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ *
+ * context.h: Parameterized context test case, kernel side.
+ */
+#ifndef _CONTEXT_H
+#define _CONTEXT_H
+
+#include "context_self.h"
+
+void add_context_tests(void);
+void context_tests_cleanup(void);
+
+#endif
diff --git a/tools/testing/selftests/ktf/selftest/context_self.h b/tools/testing/selftests/ktf/selftest/context_self.h
new file mode 100644
index 0000000..3939559
--- /dev/null
+++ b/tools/testing/selftests/ktf/selftest/context_self.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ *
+ * context_self.h: The data structure passed between user level and kernel for the
+ * hybrid self tests. Included both from user space and kernel space and
+ * needs to be a C struct.
+ */
+
+#ifndef KTF_CONTEXT_SELF_H
+#define KTF_CONTEXT_SELF_H
+
+#define CONTEXT_SELF_MAX_TEXT 30
+
+/* A simple example parameter block:
+ * For verification purposes it can be useful to have a field
+ * like 'magic' below, which serves for the purpose of
+ * a sanity check that the parameters sent by the user program
+ * actually corresponds to what the kernel expects:
+ */
+struct test_parameter_block {
+ long magic;
+ long myvalue;
+ char s[CONTEXT_SELF_MAX_TEXT+1];
+};
+
+/* Constants for the selftest.param_context test: */
+#define CONTEXT_MSG "from user to kernel"
+#define CONTEXT_MAGIC1 0xfaaa1234UL
+#define CONTEXT_MAGIC2 0xaabbccUL
+#define CONTEXT_MAGIC3 0x123456UL
+
+#endif
diff --git a/tools/testing/selftests/ktf/selftest/hybrid.c b/tools/testing/selftests/ktf/selftest/hybrid.c
new file mode 100644
index 0000000..999a7d8
--- /dev/null
+++ b/tools/testing/selftests/ktf/selftest/hybrid.c
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ *
+ * hybrid.c: Hybrid (combined user level and kernel) self tests,
+ * kernel side:
+ */
+
+#include "ktf.h"
+#include "hybrid.h"
+
+/* First a simple message passing test that just verifies that we receive
+ * "out-of-band" data from user space:
+ */
+
+TEST(selftest, msg)
+{
+ /* Accept data of type 'struct hybrid_self_params' (defined in hybrid_self.h)
+ * from user mode. This functionality is to allow user mode to test something,
+ * for instance that a certain parameter is handled in a specific way in the kernel.
+ * The user then has the option to provide data to the kernel out-of-band to
+ * tell the kernel side what to expect.
+ * In this test, just verify that data has been transmitted correctly:
+ */
+ KTF_USERDATA(self, hybrid_self_params, data);
+
+ EXPECT_STREQ(data->text_val, HYBRID_MSG);
+ EXPECT_LONG_EQ(data->val, HYBRID_MSG_VAL);
+}
+
+void add_hybrid_tests(void)
+{
+ ADD_TEST(msg);
+}
diff --git a/tools/testing/selftests/ktf/selftest/hybrid.h b/tools/testing/selftests/ktf/selftest/hybrid.h
new file mode 100644
index 0000000..0ba6f72
--- /dev/null
+++ b/tools/testing/selftests/ktf/selftest/hybrid.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ *
+ * hybrid.h: Hybrid (combined user level and kernel) self tests,
+ * kernel side, internal interface:
+ */
+
+#ifndef KTF_HYBRID_H
+#define KTF_HYBRID_H
+
+#include "hybrid_self.h"
+
+/* The kernel part of hybrid tests must be added to KTFs set of tests like any other tests,
+ * in fact from KTF's kernel perspective it is like any other test, except that it likely will
+ * fail if called without the context provided from the user space side.
+ *
+ * This function adds the tests declared in hybrid.c
+ */
+void add_hybrid_tests(void);
+
+
+#endif
diff --git a/tools/testing/selftests/ktf/selftest/hybrid_self.h b/tools/testing/selftests/ktf/selftest/hybrid_self.h
new file mode 100644
index 0000000..21c6c92
--- /dev/null
+++ b/tools/testing/selftests/ktf/selftest/hybrid_self.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ *
+ * hybrid_self.h: The data structure passed between user level and kernel for the
+ * hybrid self tests. Included both from user space and kernel space and
+ * needs to be a C struct.
+ */
+
+#ifndef KTF_HYBRID_SELF_H
+#define KTF_HYBRID_SELF_H
+
+#define HYBRID_SELF_MAX_TEXT 127
+
+struct hybrid_self_params
+{
+ char text_val[HYBRID_SELF_MAX_TEXT+1];
+ unsigned long val;
+};
+
+
+/* Constants for the selftest.msg test: */
+#define HYBRID_MSG "a little test string"
+#define HYBRID_MSG_VAL 0xffUL
+
+#endif
diff --git a/tools/testing/selftests/ktf/selftest/ktf_syms.txt b/tools/testing/selftests/ktf/selftest/ktf_syms.txt
new file mode 100644
index 0000000..721ae98
--- /dev/null
+++ b/tools/testing/selftests/ktf/selftest/ktf_syms.txt
@@ -0,0 +1,17 @@
+#module ktf
+#header ktf_map.h
+ktf_map_init
+ktf_map_elem_init
+ktf_map_insert
+ktf_map_find
+ktf_map_find_first
+ktf_map_remove
+ktf_map_elem_get
+ktf_map_elem_put
+ktf_map_find_next
+ktf_map_delete_all
+#header ktf_cov.h
+ktf_cov_entry_find
+ktf_cov_entry_put
+ktf_cov_enable
+ktf_cov_disable
diff --git a/tools/testing/selftests/ktf/selftest/self.c b/tools/testing/selftests/ktf/selftest/self.c
new file mode 100644
index 0000000..8b7a582
--- /dev/null
+++ b/tools/testing/selftests/ktf/selftest/self.c
@@ -0,0 +1,661 @@
+/*
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ *
+ * self.c: Some simple self tests for KTF
+ */
+#include <linux/module.h>
+#include <linux/mm_types.h>
+#include <linux/slab.h>
+#include <linux/slab_def.h>
+
+#include "ktf.h"
+#include "ktf_map.h"
+#include "ktf_cov.h"
+#include "ktf_syms.h"
+
+#include "hybrid.h"
+#include "context.h"
+
+MODULE_LICENSE("GPL");
+
+struct map_test_ctx {
+ struct ktf_context k;
+};
+
+static struct map_test_ctx s_mctx[4];
+
+/* Declare a simple handle with no contexts for simple (unparameterized) tests: */
+KTF_INIT();
+
+/* For tests that defines multiple test cases
+ * (e.g. if the test scope requires application of each test on several devices or
+ * other abstract contexts, definable by the test module)
+ */
+static KTF_HANDLE_INIT(dual_handle);
+static KTF_HANDLE_INIT(single_handle);
+static KTF_HANDLE_INIT(no_handle);
+static KTF_HANDLE_INIT_VERSION(wrongversion_handle, 0, false);
+
+static struct map_test_ctx *to_mctx(struct ktf_context *ctx)
+{
+ return container_of(ctx, struct map_test_ctx, k);
+}
+
+struct myelem {
+ struct ktf_map_elem foo;
+ int freed;
+ int order;
+};
+
+/* --- Simple insertion and removal test --- */
+
+TEST(selftest, simplemap)
+{
+ int i;
+ const int nelems = 3;
+ struct map_test_ctx *mctx = to_mctx(ctx);
+ struct ktf_map tm;
+ struct myelem e[nelems];
+
+ if (mctx)
+ tlog(T_DEBUG, "ctx %s", mctx->k.elem.key);
+ else
+ tlog(T_DEBUG, "ctx <none>");
+
+ ktf_map_init(&tm, NULL, NULL);
+ EXPECT_INT_EQ(0, ktf_map_elem_init(&e[0].foo, "foo"));
+ EXPECT_INT_EQ(0, ktf_map_elem_init(&e[1].foo, "bar"));
+ EXPECT_INT_EQ(0, ktf_map_elem_init(&e[2].foo, "zax"));
+
+ for (i = 0; i < nelems; i++) {
+ EXPECT_LONG_EQ(i, ktf_map_size(&tm));
+ EXPECT_INT_EQ(0, ktf_map_insert(&tm, &e[i].foo));
+ }
+ EXPECT_LONG_EQ(i, ktf_map_size(&tm));
+
+ /* Should be sorted alphabetically so we get 'bar' back: */
+ EXPECT_ADDR_EQ(&e[1].foo, ktf_map_find_first(&tm));
+
+ for (i = 0; i < nelems; i++) {
+ EXPECT_LONG_EQ(nelems - i, ktf_map_size(&tm));
+ EXPECT_ADDR_EQ(&e[i].foo, ktf_map_remove(&tm, e[i].foo.key));
+ }
+ EXPECT_LONG_EQ(0, ktf_map_size(&tm));
+}
+
+/* --- Reference counting test --- */
+
+/* should be called when refcount is 0. */
+static void myelem_free(struct ktf_map_elem *elem)
+{
+ struct myelem *myelem = container_of(elem, struct myelem, foo);
+
+ myelem->freed = 1;
+}
+
+TEST(selftest, mapref)
+{
+ int i;
+ const int nelems = 3;
+ struct myelem e[nelems], *ep;
+ struct ktf_map tm;
+ struct ktf_map_elem *elem;
+
+ ktf_map_init(&tm, NULL, myelem_free);
+ /* Init map elems with "foo" "bar" "zax" */
+ EXPECT_INT_EQ(0, ktf_map_elem_init(&e[0].foo, "foo"));
+ EXPECT_INT_EQ(0, ktf_map_elem_init(&e[1].foo, "bar"));
+ EXPECT_INT_EQ(0, ktf_map_elem_init(&e[2].foo, "zax"));
+
+ /* Insert elems and drop our refcounts (map still holds ref) */
+ for (i = 0; i < nelems; i++) {
+ EXPECT_INT_EQ(0, ktf_map_insert(&tm, &e[i].foo));
+ ktf_map_elem_put(&e[i].foo);
+ }
+
+ /* This macro takes (and drops) refcount for each elem */
+ ktf_map_for_each_entry(ep, &tm, foo)
+ ep->freed = 0;
+
+ for (i = 0; i < nelems; i++) {
+ elem = ktf_map_remove(&tm, e[i].foo.key);
+ EXPECT_INT_EQ(0, e[i].freed);
+ /* free our ref, now free function should be called. */
+ ktf_map_elem_put(elem);
+ EXPECT_INT_EQ(1, e[i].freed);
+ }
+
+ ktf_map_delete_all(&tm);
+ EXPECT_LONG_EQ(0, ktf_map_size(&tm));
+}
+
+/* --- Test that the expect macros work as if-then-else single statement */
+TEST(selftest, statements)
+{
+ char c;
+ char *cp = &c;
+ /* These are mostly intended as compilation syntax tests */
+ if (_i)
+ EXPECT_TRUE(true);
+ else
+ EXPECT_FALSE(false);
+ if (_i)
+ ASSERT_TRUE(true);
+ else
+ ASSERT_FALSE(false);
+ if (_i)
+ ASSERT_OK_ADDR(cp);
+ else
+ ASSERT_OK_ADDR_GOTO(cp, out);
+ if (_i)
+ ASSERT_OK_ADDR_BREAK(cp);
+out:
+ EXPECT_TRUE(true);
+}
+
+/* --- Compare function test --- */
+
+/* key comparison function */
+static int myelem_cmp(const char *key1, const char *key2)
+{
+ int i1 = *((int *)key1);
+ int i2 = *((int *)key2);
+
+ if (i1 < i2)
+ return -1;
+ else if (i1 > i2)
+ return 1;
+ return 0;
+}
+
+TEST(selftest, mapcmpfunc)
+{
+ int i;
+ const int nelems = 3;
+ struct myelem e[nelems], *ep;
+ struct ktf_map tm;
+
+ ktf_map_init(&tm, myelem_cmp, NULL);
+ /* Init map elems with keys "foo" "bar" "zax" */
+ EXPECT_INT_EQ(0, ktf_map_elem_init(&e[0].foo, "foo"));
+ EXPECT_INT_EQ(0, ktf_map_elem_init(&e[1].foo, "bar"));
+ EXPECT_INT_EQ(0, ktf_map_elem_init(&e[2].foo, "zax"));
+
+ /* Insert elems with order values 3, 2, 1. Ensure we see order
+ * 1, 2, 3 on retrieval.
+ */
+ for (i = 0; i < nelems; i++) {
+ e[i].order = nelems - i;
+ EXPECT_INT_EQ(0, ktf_map_elem_init(&e[i].foo,
+ (char *)&e[i].order));
+ EXPECT_INT_EQ(0, ktf_map_insert(&tm, &e[i].foo));
+ }
+ i = 1;
+ /* Ensure ordering via compare function is respected */
+ ktf_map_for_each_entry(ep, &tm, foo)
+ EXPECT_INT_EQ(ep->order, i++);
+
+ ktf_map_delete_all(&tm);
+ EXPECT_LONG_EQ(0, ktf_map_size(&tm));
+}
+
+/* --- Verify that key name is truncated at KTF_MAX_NAME length --- */
+
+TEST(selftest, map_keyoverflow)
+{
+ struct myelem e;
+ struct ktf_map tm;
+ char jumbokey[KTF_MAX_NAME + 2];
+ char jumbokey_truncated[KTF_MAX_NAME + 1];
+
+ ktf_map_init(&tm, NULL, NULL);
+ memset(jumbokey, 'x', KTF_MAX_NAME + 1);
+ memset(jumbokey_truncated, 'x', KTF_MAX_NAME);
+ jumbokey_truncated[KTF_MAX_NAME] = '\0';
+ EXPECT_INT_EQ(0, ktf_map_elem_init(&e.foo, jumbokey));
+ EXPECT_TRUE(strcmp(e.foo.key, jumbokey_truncated) == 0);
+}
+
+struct mykey {
+ unsigned long address;
+ unsigned long size;
+};
+
+/* Comparison here is to check if k1's address falls in range
+ * [k2->address, k2->address + k2->size]. Similar compare used in
+ * ktf_cov to figure out if a function address lies within the function
+ * code.
+ */
+static int custom_compare(const char *key1, const char *key2)
+{
+ struct mykey *k1 = (struct mykey *)key1;
+ struct mykey *k2 = (struct mykey *)key2;
+
+ if (k1->address < k2->address)
+ return -1;
+ if (k1->address >= (k2->address + k2->size))
+ return 1;
+ return 0;
+}
+
+/* --- Verify that opaque keys with custom compare function work --- */
+
+TEST(selftest, map_customkey)
+{
+ const int nelems = 3;
+ int baseaddr = 1024;
+ struct ktf_map cm;
+ struct mykey keys[nelems], search;
+ struct myelem elems[nelems];
+ int i, j;
+
+ ktf_map_init(&cm, custom_compare, NULL);
+
+ /* Ensure we can add entries and then retrieve them via search key. */
+ for (i = 0; i < nelems; i++) {
+ baseaddr += (i << 2);
+ keys[i].address = baseaddr;
+ keys[i].size = (i + 1) << 2;
+ ASSERT_INT_EQ_GOTO(ktf_map_elem_init(&elems[i].foo,
+ (char *)&keys[i]),
+ 0, done);
+ ASSERT_INT_EQ_GOTO(ktf_map_insert(&cm, &elems[i].foo), 0, done);
+ }
+
+ baseaddr = 1024;
+
+ /* Ensure all search addresses within range of [base address, size]
+ * find appropriate entries.
+ */
+ for (i = 0; i < nelems; i++) {
+ baseaddr += (i << 2);
+ for (j = 0; j < (i + 1) << 2; j++) {
+ search.address = baseaddr + j;
+ search.size = 0;
+ ASSERT_ADDR_EQ_GOTO(ktf_map_find_entry(&cm,
+ (char *)&search,
+ struct myelem,
+ foo),
+ &elems[i], done);
+ }
+ }
+
+done:
+ ktf_map_delete_all(&cm);
+}
+
+TEST(selftest, dummy)
+{
+ /* The default handle does not have any contexts in this test set */
+ ASSERT_FALSE(ctx);
+}
+
+TEST(selftest, wrongversion)
+{
+ tlog(T_INFO, "This test should never have run - wrong version\n!!!");
+ EXPECT_TRUE(false);
+}
+
+static void add_map_tests(void)
+{
+ ADD_TEST(dummy);
+ ADD_LOOP_TEST(statements, 0, 2);
+ ADD_TEST_TO(dual_handle, simplemap);
+ ADD_TEST_TO(dual_handle, mapref);
+ ADD_TEST_TO(dual_handle, mapcmpfunc);
+ ADD_TEST(map_keyoverflow);
+ ADD_TEST(map_customkey);
+
+ terr("-- version check test: --");
+ /* This should fail */
+ ADD_TEST_TO(wrongversion_handle, wrongversion);
+}
+
+static int probecount;
+static int proberet;
+
+KTF_ENTRY_PROBE(printk, printkhandler)
+{
+ probecount++;
+
+ KTF_ENTRY_PROBE_RETURN(0);
+}
+
+static int entryarg0, entryarg1;
+
+KTF_ENTRY_PROBE(probeargtest, probeargtesthandler)
+{
+ entryarg0 = (int)KTF_ENTRY_PROBE_ARG0;
+ entryarg1 = (int)KTF_ENTRY_PROBE_ARG1;
+ KTF_ENTRY_PROBE_RETURN(0);
+}
+
+noinline void probeargtest(int arg0, int arg1)
+{
+ tlog(T_INFO, "got args %d, %d\n", arg0, arg1);
+}
+
+TEST(selftest, probeentry)
+{
+ probecount = 0;
+ ASSERT_INT_EQ(KTF_REGISTER_ENTRY_PROBE(printk, printkhandler), 0);
+ /* Need T_WARN for unconditional printk() */
+ twarn("Testing kprobe entry...");
+ ASSERT_INT_GT_GOTO(probecount, 0, done);
+ ASSERT_INT_EQ_GOTO(KTF_REGISTER_ENTRY_PROBE(probeargtest,
+ probeargtesthandler),
+ 0, done);
+ probeargtest(1, 2);
+ ASSERT_INT_EQ_GOTO(entryarg0, 1, done);
+ ASSERT_INT_EQ_GOTO(entryarg1, 2, done);
+done:
+ KTF_UNREGISTER_ENTRY_PROBE(probeargtest, probeargtesthandler);
+ KTF_UNREGISTER_ENTRY_PROBE(printk, printkhandler);
+}
+
+static int override_failed;
+
+noinline int myfunc(int i)
+{
+ override_failed = 1;
+ return i;
+}
+
+KTF_OVERRIDE(myfunc, myfunc_override)
+{
+ KTF_SET_RETURN_VALUE(0);
+ KTF_OVERRIDE_RETURN;
+}
+
+TEST(selftest, override)
+{
+ override_failed = 0;
+
+ ASSERT_INT_EQ(KTF_REGISTER_OVERRIDE(myfunc, myfunc_override), 0);
+
+ (void)myfunc(0);
+
+ /* Verify override function runs instead. */
+ ASSERT_TRUE_GOTO(override_failed == 0, done);
+
+ /* Verify override function modifies return value. */
+ ASSERT_INT_EQ_GOTO(myfunc(100), 0, done);
+ ASSERT_TRUE_GOTO(override_failed == 0, done);
+done:
+ KTF_UNREGISTER_OVERRIDE(myfunc, myfunc_override);
+}
+
+noinline int probesum(int a, int b)
+{
+ tlog(T_INFO, "Adding %d + %d", a, b);
+ return a + b;
+}
+
+KTF_RETURN_PROBE(probesum, probesumhandler)
+{
+ tlog(T_DEBUG, "return value before modifying %ld",
+ regs_return_value(regs));
+ KTF_SET_RETURN_VALUE(-1);
+ tlog(T_DEBUG, "return value after modifying %ld",
+ regs_return_value(regs));
+ return 0;
+}
+
+KTF_RETURN_PROBE(printk, printkrethandler)
+{
+ proberet = KTF_RETURN_VALUE();
+
+ return 0;
+}
+
+TEST(selftest, probereturn)
+{
+ char *teststr = "Testing kprobe return...";
+
+ proberet = -1;
+ ASSERT_INT_EQ_GOTO(KTF_REGISTER_RETURN_PROBE(printk, printkrethandler),
+ 0, done);
+ printk(KERN_INFO "%s", teststr);
+ ASSERT_INT_EQ_GOTO(proberet, strlen(teststr), done);
+
+ /* Now test modification of return value */
+ ASSERT_INT_EQ_GOTO(probesum(1, 1), 2, done);
+ ASSERT_INT_EQ_GOTO(KTF_REGISTER_RETURN_PROBE(probesum, probesumhandler),
+ 0, done);
+ ASSERT_INT_EQ_GOTO(probesum(1, 1), -1, done);
+done:
+ KTF_UNREGISTER_RETURN_PROBE(printk, printkrethandler);
+ KTF_UNREGISTER_RETURN_PROBE(probesum, probesumhandler);
+}
+
+static void add_probe_tests(void)
+{
+ ADD_TEST(probeentry);
+ ADD_TEST(probereturn);
+ ADD_TEST(override);
+}
+
+noinline void cov_counted(void)
+{
+ tlog(T_INFO, "got called!");
+}
+
+noinline void *doalloc(struct kmem_cache *c, size_t sz)
+{
+ if (c)
+ return kmem_cache_alloc(c, GFP_KERNEL);
+ return kmalloc(sz, GFP_KERNEL);
+}
+
+TEST(selftest, acov)
+{
+ /* A very basic test just to enable and disable the coverage support,
+ * without the memory tracking option and without making use of it:
+ */
+ ASSERT_INT_EQ(0, ktf_cov_enable((THIS_MODULE)->name, 0));
+ ktf_cov_disable((THIS_MODULE)->name);
+}
+
+TEST(selftest, cov)
+{
+ int foundp1 = 0, foundp2 = 0, foundp3 = 0, foundp4 = 0;
+ struct ktf_cov_entry *e;
+ struct ktf_cov_mem *m;
+ char *p1 = NULL, *p2 = NULL, *p3 = NULL, *p4 = NULL;
+ struct kmem_cache *c = NULL;
+ int oldcount;
+
+ c = kmem_cache_create("selftest_cov_cache",
+ 32, 0,
+ SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL);
+
+ ASSERT_ADDR_NE(NULL, c);
+
+ tlog(T_INFO, "Allocated cache %p : %s %u\n", c, c->name, c->object_size);
+ ASSERT_INT_EQ(0, ktf_cov_enable((THIS_MODULE)->name, KTF_COV_OPT_MEM));
+
+ e = ktf_cov_entry_find((unsigned long)cov_counted, 0);
+ ASSERT_ADDR_NE_GOTO(e, NULL, done);
+ oldcount = e->count;
+ ktf_cov_entry_put(e);
+ cov_counted();
+ e = ktf_cov_entry_find((unsigned long)cov_counted, 0);
+ ASSERT_ADDR_NE_GOTO(e, NULL, done);
+ if (e) {
+ ASSERT_INT_EQ(e->count, oldcount + 1);
+ ktf_cov_entry_put(e);
+ }
+
+ /* Need to call a noinline fn to do allocs since this test function
+ * will be inlined; and to track allocations they need to come
+ * from this module. Don't need to do the same for kfree since
+ * we check every kfree() to see if it is freeing a tracked allocation.
+ */
+ p1 = doalloc(NULL, 8);
+ ASSERT_ADDR_NE_GOTO(p1, NULL, done);
+ p2 = doalloc(NULL, 16);
+ ASSERT_ADDR_NE_GOTO(p2, NULL, done);
+ p3 = doalloc(c, 0);
+ ASSERT_ADDR_NE_GOTO(p3, NULL, done);
+ p4 = doalloc(c, 0);
+ ASSERT_ADDR_NE_GOTO(p4, NULL, done);
+
+ ktf_for_each_cov_mem(m) {
+ if (m->key.address == (unsigned long)p1)
+ foundp1 = 1;
+ if (m->key.address == (unsigned long)p2 && m->key.size == 16)
+ foundp2 = 1;
+ if (m->key.address == (unsigned long)p3 && m->key.size == 32)
+ foundp3 = 1;
+ if (m->key.address == (unsigned long)p4)
+ foundp4 = 1;
+ }
+ ASSERT_INT_EQ_GOTO(foundp1, 1, done);
+ ASSERT_INT_EQ_GOTO(foundp2, 1, done);
+ ASSERT_INT_EQ_GOTO(foundp3, 1, done);
+ ASSERT_INT_EQ_GOTO(foundp4, 1, done);
+ kfree(p1);
+ kmem_cache_free(c, p4);
+ /* Didn't free p2/p3 - should still be on our cov_mem list */
+ foundp1 = 0;
+ foundp2 = 0;
+ foundp3 = 0;
+ foundp4 = 0;
+ ktf_for_each_cov_mem(m) {
+ if (m->key.address == (unsigned long)p1)
+ foundp1 = 1;
+ if (m->key.address == (unsigned long)p2)
+ foundp2 = 1;
+ if (m->key.address == (unsigned long)p3)
+ foundp3 = 1;
+ if (m->key.address == (unsigned long)p4)
+ foundp4 = 1;
+ }
+ ASSERT_INT_EQ_GOTO(foundp2, 1, done);
+ ASSERT_INT_EQ_GOTO(foundp3, 1, done);
+ ASSERT_INT_EQ_GOTO(foundp1, 0, done);
+ ASSERT_INT_EQ_GOTO(foundp4, 0, done);
+done:
+ kfree(p2);
+ if (p3)
+ kmem_cache_free(c, p3);
+ ktf_cov_disable((THIS_MODULE)->name);
+ kmem_cache_destroy(c);
+}
+
+static void add_cov_tests(void)
+{
+ ADD_TEST(acov);
+ /* We still seem to have some subtle issues with the memory coverage test feature,
+ * as sometimes allocations made by the coverage framework itself,
+ * for this particular test survives the cleanup function.
+ * Whether it is our attempt to test ourselves or a more generic problem
+ * is not fully understood yet, so disable this test for now:
+ */
+ /* ADD_TEST(cov); */
+}
+
+KTF_THREAD(test_thread)
+{
+ /* ensure assertions can work in thread context */
+ ASSERT_INT_EQ(1, 1);
+}
+
+#define NUM_TEST_THREADS 20
+
+static struct ktf_thread test_threads[NUM_TEST_THREADS];
+
+TEST(selftest, thread)
+{
+ int assertions, i;
+
+ for (i = 0; i < NUM_TEST_THREADS; i++) {
+ KTF_THREAD_INIT(test_thread, &test_threads[i]);
+ KTF_THREAD_RUN(&test_threads[i]);
+ }
+ for (i = 0; i < NUM_TEST_THREADS; i++)
+ KTF_THREAD_WAIT_COMPLETED(&test_threads[i]);
+
+ assertions = (int)ktf_get_assertion_count();
+
+ /* Verify assertion in thread */
+ ASSERT_INT_EQ(assertions, NUM_TEST_THREADS);
+}
+
+static void add_thread_tests(void)
+{
+ ADD_TEST(thread);
+}
+
+static int selftest_module_var;
+
+/*
+ * Test that ktf_find_symbol works both for module symbols and
+ * core kernel symbols:
+ */
+TEST(selftest, symbol)
+{
+ /* Verify finding kernel-internal symbol works. */
+ ASSERT_ADDR_NE(ktf_find_symbol(NULL, "skbuff_head_cache"), NULL);
+
+ /* Verify finding module symbols works, both when we specify the
+ * module name and when we don't.
+ */
+ ASSERT_ADDR_EQ(ktf_find_symbol(NULL, "selftest_module_var"),
+ &selftest_module_var);
+
+ ASSERT_ADDR_EQ(ktf_find_symbol("selftest", "selftest_module_var"),
+ &selftest_module_var);
+}
+
+static void add_symbol_tests(void)
+{
+ ADD_TEST(symbol);
+}
+
+static int __init selftest_init(void)
+{
+ int ret = KTF_CONTEXT_ADD_TO(dual_handle, &s_mctx[1].k, "map1");
+
+ tlog(T_DEBUG, "map1 gets %d", ret);
+ if (ret)
+ return ret;
+
+ ret = KTF_CONTEXT_ADD_TO(dual_handle, &s_mctx[2].k, "map2");
+ if (ret)
+ goto fail;
+
+ ret = KTF_CONTEXT_ADD_TO(single_handle, &s_mctx[3].k, "map3");
+ if (ret)
+ goto fail;
+
+ ktf_resolve_symbols();
+
+ add_map_tests();
+ add_probe_tests();
+ add_cov_tests();
+ add_thread_tests();
+ add_hybrid_tests();
+ add_context_tests();
+ add_symbol_tests();
+ tlog(T_INFO, "selftest: loaded");
+ return 0;
+fail:
+ KTF_CLEANUP();
+ return ret;
+}
+
+static void __exit selftest_exit(void)
+{
+ context_tests_cleanup();
+ KTF_HANDLE_CLEANUP(single_handle);
+ KTF_HANDLE_CLEANUP(dual_handle);
+ KTF_HANDLE_CLEANUP(no_handle);
+ KTF_CLEANUP();
+ tlog(T_INFO, "selftest: unloaded");
+}
+
+module_init(selftest_init);
+module_exit(selftest_exit);
--
git-series 0.9.1