Linux Audio

Check our new training course

Real-Time Linux with PREEMPT_RT training

Feb 18-20, 2025
Register
Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * test_fprobe.c - simple sanity test for fprobe
  4 */
  5
  6#include <linux/kernel.h>
  7#include <linux/fprobe.h>
  8#include <linux/random.h>
  9#include <kunit/test.h>
 10
 11#define div_factor 3
 12
 13static struct kunit *current_test;
 14
 15static u32 rand1, entry_val, exit_val;
 16
 17/* Use indirect calls to avoid inlining the target functions */
 18static u32 (*target)(u32 value);
 19static u32 (*target2)(u32 value);
 20static u32 (*target_nest)(u32 value, u32 (*nest)(u32));
 21static unsigned long target_ip;
 22static unsigned long target2_ip;
 23static unsigned long target_nest_ip;
 24static int entry_return_value;
 25
 26static noinline u32 fprobe_selftest_target(u32 value)
 27{
 28	return (value / div_factor);
 29}
 30
 31static noinline u32 fprobe_selftest_target2(u32 value)
 32{
 33	return (value / div_factor) + 1;
 34}
 35
 36static noinline u32 fprobe_selftest_nest_target(u32 value, u32 (*nest)(u32))
 37{
 38	return nest(value + 2);
 39}
 40
 41static notrace int fp_entry_handler(struct fprobe *fp, unsigned long ip,
 42				    unsigned long ret_ip,
 43				    struct pt_regs *regs, void *data)
 44{
 45	KUNIT_EXPECT_FALSE(current_test, preemptible());
 46	/* This can be called on the fprobe_selftest_target and the fprobe_selftest_target2 */
 47	if (ip != target_ip)
 48		KUNIT_EXPECT_EQ(current_test, ip, target2_ip);
 49	entry_val = (rand1 / div_factor);
 50	if (fp->entry_data_size) {
 51		KUNIT_EXPECT_NOT_NULL(current_test, data);
 52		if (data)
 53			*(u32 *)data = entry_val;
 54	} else
 55		KUNIT_EXPECT_NULL(current_test, data);
 56
 57	return entry_return_value;
 58}
 59
 60static notrace void fp_exit_handler(struct fprobe *fp, unsigned long ip,
 61				    unsigned long ret_ip,
 62				    struct pt_regs *regs, void *data)
 63{
 64	unsigned long ret = regs_return_value(regs);
 65
 66	KUNIT_EXPECT_FALSE(current_test, preemptible());
 67	if (ip != target_ip) {
 68		KUNIT_EXPECT_EQ(current_test, ip, target2_ip);
 69		KUNIT_EXPECT_EQ(current_test, ret, (rand1 / div_factor) + 1);
 70	} else
 71		KUNIT_EXPECT_EQ(current_test, ret, (rand1 / div_factor));
 72	KUNIT_EXPECT_EQ(current_test, entry_val, (rand1 / div_factor));
 73	exit_val = entry_val + div_factor;
 74	if (fp->entry_data_size) {
 75		KUNIT_EXPECT_NOT_NULL(current_test, data);
 76		if (data)
 77			KUNIT_EXPECT_EQ(current_test, *(u32 *)data, entry_val);
 78	} else
 79		KUNIT_EXPECT_NULL(current_test, data);
 80}
 81
 82static notrace int nest_entry_handler(struct fprobe *fp, unsigned long ip,
 83				      unsigned long ret_ip,
 84				      struct pt_regs *regs, void *data)
 85{
 86	KUNIT_EXPECT_FALSE(current_test, preemptible());
 87	return 0;
 88}
 89
 90static notrace void nest_exit_handler(struct fprobe *fp, unsigned long ip,
 91				      unsigned long ret_ip,
 92				      struct pt_regs *regs, void *data)
 93{
 94	KUNIT_EXPECT_FALSE(current_test, preemptible());
 95	KUNIT_EXPECT_EQ(current_test, ip, target_nest_ip);
 96}
 97
 98/* Test entry only (no rethook) */
 99static void test_fprobe_entry(struct kunit *test)
100{
101	struct fprobe fp_entry = {
102		.entry_handler = fp_entry_handler,
103	};
104
105	current_test = test;
106
107	/* Before register, unregister should be failed. */
108	KUNIT_EXPECT_NE(test, 0, unregister_fprobe(&fp_entry));
109	KUNIT_EXPECT_EQ(test, 0, register_fprobe(&fp_entry, "fprobe_selftest_target*", NULL));
110
111	entry_val = 0;
112	exit_val = 0;
113	target(rand1);
114	KUNIT_EXPECT_NE(test, 0, entry_val);
115	KUNIT_EXPECT_EQ(test, 0, exit_val);
116
117	entry_val = 0;
118	exit_val = 0;
119	target2(rand1);
120	KUNIT_EXPECT_NE(test, 0, entry_val);
121	KUNIT_EXPECT_EQ(test, 0, exit_val);
122
123	KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp_entry));
124}
125
126static void test_fprobe(struct kunit *test)
127{
128	struct fprobe fp = {
129		.entry_handler = fp_entry_handler,
130		.exit_handler = fp_exit_handler,
131	};
132
133	current_test = test;
134	KUNIT_EXPECT_EQ(test, 0, register_fprobe(&fp, "fprobe_selftest_target*", NULL));
135
136	entry_val = 0;
137	exit_val = 0;
138	target(rand1);
139	KUNIT_EXPECT_NE(test, 0, entry_val);
140	KUNIT_EXPECT_EQ(test, entry_val + div_factor, exit_val);
141
142	entry_val = 0;
143	exit_val = 0;
144	target2(rand1);
145	KUNIT_EXPECT_NE(test, 0, entry_val);
146	KUNIT_EXPECT_EQ(test, entry_val + div_factor, exit_val);
147
148	KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp));
149}
150
151static void test_fprobe_syms(struct kunit *test)
152{
153	static const char *syms[] = {"fprobe_selftest_target", "fprobe_selftest_target2"};
154	struct fprobe fp = {
155		.entry_handler = fp_entry_handler,
156		.exit_handler = fp_exit_handler,
157	};
158
159	current_test = test;
160	KUNIT_EXPECT_EQ(test, 0, register_fprobe_syms(&fp, syms, 2));
161
162	entry_val = 0;
163	exit_val = 0;
164	target(rand1);
165	KUNIT_EXPECT_NE(test, 0, entry_val);
166	KUNIT_EXPECT_EQ(test, entry_val + div_factor, exit_val);
167
168	entry_val = 0;
169	exit_val = 0;
170	target2(rand1);
171	KUNIT_EXPECT_NE(test, 0, entry_val);
172	KUNIT_EXPECT_EQ(test, entry_val + div_factor, exit_val);
173
174	KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp));
175}
176
177/* Test private entry_data */
178static void test_fprobe_data(struct kunit *test)
179{
180	struct fprobe fp = {
181		.entry_handler = fp_entry_handler,
182		.exit_handler = fp_exit_handler,
183		.entry_data_size = sizeof(u32),
184	};
185
186	current_test = test;
187	KUNIT_EXPECT_EQ(test, 0, register_fprobe(&fp, "fprobe_selftest_target", NULL));
188
189	target(rand1);
190
191	KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp));
192}
193
194/* Test nr_maxactive */
195static void test_fprobe_nest(struct kunit *test)
196{
197	static const char *syms[] = {"fprobe_selftest_target", "fprobe_selftest_nest_target"};
198	struct fprobe fp = {
199		.entry_handler = nest_entry_handler,
200		.exit_handler = nest_exit_handler,
201		.nr_maxactive = 1,
202	};
203
204	current_test = test;
205	KUNIT_EXPECT_EQ(test, 0, register_fprobe_syms(&fp, syms, 2));
206
207	target_nest(rand1, target);
208	KUNIT_EXPECT_EQ(test, 1, fp.nmissed);
209
210	KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp));
211}
212
213static void test_fprobe_skip(struct kunit *test)
214{
215	struct fprobe fp = {
216		.entry_handler = fp_entry_handler,
217		.exit_handler = fp_exit_handler,
218	};
219
220	current_test = test;
221	KUNIT_EXPECT_EQ(test, 0, register_fprobe(&fp, "fprobe_selftest_target", NULL));
222
223	entry_return_value = 1;
224	entry_val = 0;
225	exit_val = 0;
226	target(rand1);
227	KUNIT_EXPECT_NE(test, 0, entry_val);
228	KUNIT_EXPECT_EQ(test, 0, exit_val);
229	KUNIT_EXPECT_EQ(test, 0, fp.nmissed);
230	entry_return_value = 0;
231
232	KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp));
233}
234
235static unsigned long get_ftrace_location(void *func)
236{
237	unsigned long size, addr = (unsigned long)func;
238
239	if (!kallsyms_lookup_size_offset(addr, &size, NULL) || !size)
240		return 0;
241
242	return ftrace_location_range(addr, addr + size - 1);
243}
244
245static int fprobe_test_init(struct kunit *test)
246{
247	rand1 = get_random_u32_above(div_factor);
248	target = fprobe_selftest_target;
249	target2 = fprobe_selftest_target2;
250	target_nest = fprobe_selftest_nest_target;
251	target_ip = get_ftrace_location(target);
252	target2_ip = get_ftrace_location(target2);
253	target_nest_ip = get_ftrace_location(target_nest);
254
255	return 0;
256}
257
258static struct kunit_case fprobe_testcases[] = {
259	KUNIT_CASE(test_fprobe_entry),
260	KUNIT_CASE(test_fprobe),
261	KUNIT_CASE(test_fprobe_syms),
262	KUNIT_CASE(test_fprobe_data),
263	KUNIT_CASE(test_fprobe_nest),
264	KUNIT_CASE(test_fprobe_skip),
265	{}
266};
267
268static struct kunit_suite fprobe_test_suite = {
269	.name = "fprobe_test",
270	.init = fprobe_test_init,
271	.test_cases = fprobe_testcases,
272};
273
274kunit_test_suites(&fprobe_test_suite);
275